Skip to content

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

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

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

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Feb 13, 2026

Copy link
Copy Markdown

User description

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)*

CodeAnt-AI Description

Show calendar cache status and allow deleting cached Google Calendar data

What Changed

  • Connected calendars now include a "last updated" cache timestamp for Google Calendar connections and that timestamp is shown in an actions dropdown for each integration.
  • Users can open a dropdown on a connected Google Calendar to see when the cache was last updated and delete the cached data; deleting shows success/error toasts and requires confirmation.
  • The server accepts cache-delete requests only for the owning user and removes cache entries; the database schema now records cache updated timestamps so existing and future cache rows have a reliable updatedAt value.
  • Calendar syncs update calendar cache timestamps so the displayed "last updated" reflects recent sync activity.

Impact

✅ Clearer cache timestamps for Google calendars
✅ Can delete cached calendar data from the UI
✅ Clear success/error feedback after cache deletion

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

* 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
Author

@CodeAnt-AI: review

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Feb 13, 2026
}

// Update SelectedCalendar.updatedAt for all calendars under this credential
await SelectedCalendarRepository.updateManyByCredentialId(this.credential.id, {});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The call to bulk-update selected calendars passes an empty data object to updateManyByCredentialId, which (via Prisma's updateMany) can throw at runtime because no fields are provided to update and, even if it didn't throw, would never actually bump updatedAt, so the comment's intent ("Update SelectedCalendar.updatedAt") is not fulfilled. [logic error]

Severity Level: Critical 🚨
- ❌ Google Calendar webhook fails when refreshing cached availability.
- ❌ SelectedCalendar.updatedAt never reflects latest cache refresh time.
- ⚠️ Cache status UI shows stale timestamps for Google calendars.
Suggested change
await SelectedCalendarRepository.updateManyByCredentialId(this.credential.id, {});
await SelectedCalendarRepository.updateManyByCredentialId(this.credential.id, {
updatedAt: new Date(),
});
Steps of Reproduction ✅
1. Set up a Cal.com environment with a real database and enable Google Calendar cache so
that Google push notifications are handled (webhook URL is
`/api/integrations/googlecalendar/webhook` as defined via `GOOGLE_WEBHOOK_URL` in
`packages/app-store/googlecalendar/lib/CalendarService.ts:40-42`).

2. Ensure at least one `SelectedCalendar` row exists for a Google credential (this is done
in normal flows via cron/selection logic, e.g. `handleCreateSelectedCalendars` in
`apps/web/app/api/cron/selected-calendars/route.ts:247-305`, which creates
`SelectedCalendar` records using `SelectedCalendarRepository.create` in
`packages/lib/server/repository/selectedCalendar.ts:66-80`).

3. Trigger a Google Calendar push notification so the webhook handler at
`packages/app-store/googlecalendar/api/webhook.ts:46` (found via Grep) executes `await
calendarServiceForCalendarCache?.fetchAvailabilityAndSetCache?.(selectedCalendars);`,
calling `GoogleCalendarService.fetchAvailabilityAndSetCache` in
`packages/app-store/googlecalendar/lib/CalendarService.ts:990-1025`.

4. Inside `fetchAvailabilityAndSetCache`, after successfully fetching availability and
calling `setAvailabilityInCache`, the code at `CalendarService.ts:1023-1024` executes
`SelectedCalendarRepository.updateManyByCredentialId(this.credential.id, {});`, which
forwards to `prisma.selectedCalendar.updateMany({ where: { credentialId }, data: {} })` in
`packages/lib/server/repository/selectedCalendar.ts:400-404`, causing Prisma to throw a
runtime validation error because `data` is empty and also failing to update `updatedAt`,
so the webhook request returns a 500 and no `SelectedCalendar.updatedAt` values are
bumped.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/app-store/googlecalendar/lib/CalendarService.ts
**Line:** 1024:1024
**Comment:**
	*Logic Error: The call to bulk-update selected calendars passes an empty data object to `updateManyByCredentialId`, which (via Prisma's `updateMany`) can throw at runtime because no fields are provided to update and, even if it didn't throw, would never actually bump `updatedAt`, so the comment's intent ("Update SelectedCalendar.updatedAt") is not fulfilled.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

fi

if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The in-place sed command uses macOS-specific syntax (sed -i '' -E), which on GNU sed (common in Linux/dev containers) is parsed incorrectly and results in no replacement being applied, so the environment file is never updated with the new URL. [logic error]

Severity Level: Major ⚠️
- ❌ Local Google webhook testing uses stale tunnel URL value.
- ⚠️ GOOGLE_WEBHOOK_URL in .env never updates on GNU sed.
- ⚠️ Confusing for Linux developers following webhook setup instructions.
Suggested change
sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
sed -i.bak "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE" && rm "$ENV_FILE.bak"
Steps of Reproduction ✅
1. Ensure you have a `.env` file at the project root (referenced as `../.env` from
`scripts/test-gcal-webhooks.sh`, see line 5 in `scripts/test-gcal-webhooks.sh`) with an
existing `GOOGLE_WEBHOOK_URL=` entry (example definition at `.env.example:150`).

2. On a Linux/dev-container environment that uses GNU sed (typical for this repository's
containers), run the helper script `scripts/test-gcal-webhooks.sh` to set up Google
Calendar webhooks; the script reaches the env update block at
`scripts/test-gcal-webhooks.sh:61-71` and executes the in-place edit at line 68: `sed -i
'' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"`.

3. On GNU sed, the `-i ''` syntax is not parsed like BSD/macOS sed; `-i` is treated as
"in-place with no backup" and the following empty argument `''` is treated as the
script/file, so sed errors instead of applying the substitution, and the
`GOOGLE_WEBHOOK_URL=` line in `.env` remains unchanged while the script continues (no `set
-e` in the script).

4. Start the app using this `.env` file; in
`packages/app-store/googlecalendar/lib/CalendarService.ts:41-42`,
`process.env.GOOGLE_WEBHOOK_URL` is read into `GOOGLE_WEBHOOK_URL_BASE` and used as the
`address` for Google webhooks in `startWatchingCalendarsInGoogle` at lines 150-160, so
local Google Calendar webhook subscriptions continue to use the stale URL instead of the
tunnel URL printed by the script.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/test-gcal-webhooks.sh
**Line:** 68:68
**Comment:**
	*Logic Error: The in-place sed command uses macOS-specific syntax (`sed -i '' -E`), which on GNU sed (common in Linux/dev containers) is parsed incorrectly and results in no replacement being applied, so the environment file is never updated with the new URL.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Env file overwrite / exposure
    The script writes the tunnel URL directly into ../.env, possibly creating or overwriting a developer's environment file. This can leak or overwrite sensitive values; at minimum set restrictive file permissions and avoid accidental commits or exposures.

  • Generic error handling
    On auth/finding failure the code throws a generic Error. That makes it harder for upstream callers (trpc/resolvers) to translate into an appropriate HTTP/TRPC error code. Consider using structured errors so the client can distinguish auth vs not-found responses.

  • Type / Schema Mismatch
    The handler now injects cacheUpdatedAt into each connected calendar object, but I don't see corresponding updates to the TRPC output schema or types. This can cause runtime type mismatches / incorrect typings for consumers of viewer.calendars.connectedCalendars. Verify the router/schema that defines the response shape (connectedCalendars output) is updated to include cacheUpdatedAt.

  • Prop propagation bug
    The wrapper computes shouldDisableConnectionModification = isDisabled || disableConnectionModification but continues to pass disableConnectionModification down to the calendar list / dropdown. The computed combined flag should be passed to child components so UI correctly reflects scope-based disabling.

  • Unnecessary / edge-case DB call
    The code always calls the CalendarCacheRepository with credentialIds even if the list is empty. Ensure the repository call is skipped for empty arrays to avoid unnecessary work or unexpected behavior from implementation details of the repository.

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI finished running the review.

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

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants