Skip to content

Conversation

ibex088
Copy link
Member

@ibex088 ibex088 commented Sep 16, 2025

What does this PR do?

  • Fixes #XXXX (GitHub issue number)
  • Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings

@ibex088 ibex088 requested review from a team as code owners September 16, 2025 09:24
Copy link
Contributor

coderabbitai bot commented Sep 16, 2025

Walkthrough

  • packages/lib/formatCalendarEvent.ts: formatClientIdFromEmails now returns a third value, team, derived from calEvent.team.members with sanitized emails (removing +clientId); otherwise undefined. formatCalEvent now destructures [attendees, organizer, team] and assigns all three onto the cloned event, always setting team (possibly undefined) when platformClientId is present. formatCalEventExtended behavior is unchanged.
  • packages/platform/atoms/hooks/useOAuthFlow.ts: In the Axios response interceptor error handler, the rejection now passes through the full AxiosError (err) instead of err.response. No exported/public function signatures were changed.

Possibly related PRs

  • fix: processWorkflowStep format cal event #23802: Also modifies packages/lib/formatCalendarEvent.ts by adding formatCalEventExtended and expanding the helper to handle ExtendedCalendarEvent, directly intersecting with the updated formatting logic.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description Check ❓ Inconclusive The PR description is a generic template with placeholders and checklist items but does not summarize the actual code changes present in this PR (for example, the team-member email sanitization in packages/lib/formatCalendarEvent.ts or the Axios error handling change in packages/platform/atoms/hooks/useOAuthFlow.ts), making it too vague for reviewers to understand what was changed and how to test it. Please update the PR description to briefly state the bug being fixed and the behavioral change, replace placeholder issue numbers with real references (or remove them), list the key files changed (e.g., packages/lib/formatCalendarEvent.ts and packages/platform/atoms/hooks/useOAuthFlow.ts), and provide concrete testing steps and expected results plus whether automated tests were added or updated.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely describes the primary functional change in this PR: removing the platform client ID from attendee lists when adding team members to a team event. The raw_summary shows formatClientIdFromEmails sanitizes team.members (removing +clientId) and formatCalEvent exposes the team field, so the title accurately reflects the main fix even though there is a secondary Axios interceptor tweak in useOAuthFlow.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hide-client-id-from-team-attendees

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@graphite-app graphite-app bot requested a review from a team September 16, 2025 09:24
@keithwillcode keithwillcode added core area: core, team members only platform Anything related to our platform plan labels Sep 16, 2025
@dosubot dosubot bot added teams area: teams, round robin, collective, managed event-types 🐛 bug Something isn't working labels Sep 16, 2025
...calEvent.team,
members: calEvent.team.members.map((member) => ({
...member,
email: member.email.replace(`+${clientId}`, ""),
Copy link
Member Author

Choose a reason for hiding this comment

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

remove the cliend it from the team members email

@@ -42,7 +42,7 @@ export const useOAuthFlow = ({ accessToken, refreshUrl, clientId, onError, onSuc

setIsRefreshing(false);
}
return Promise.reject(err.response);
return Promise.reject(err);
Copy link
Member Author

Choose a reason for hiding this comment

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

To fix the crash that occurs when changing the user from the dropdown in the example app,
Currently, we reject err.response, but on lines 62-63 of this file, we are attempting to access err.response.status, causing the crash

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/platform/atoms/hooks/useOAuthFlow.ts (1)

27-45: Fix: isRefreshing never reset on successful retry (refresh flow stays latched).

On the success path you return before setIsRefreshing(false), leaving the hook in a permanent refreshing state and blocking future refresh attempts.

Apply this diff to ensure we always reset in a finally, and to guard against undefined headers when retrying:

-        ? http.responseInterceptor.use(undefined, async (err: AxiosError) => {
+        ? http.responseInterceptor.use(undefined, async (err: AxiosError) => {
             const originalRequest = err.config as AxiosRequestConfig;
             if (refreshUrl && err.response?.status === 498 && !isRefreshing) {
-              setIsRefreshing(true);
-              const refreshedToken = await debouncedRefresh(refreshUrl);
-              if (refreshedToken) {
-                setClientAccessToken(refreshedToken);
-                onSuccess?.();
-                return http.instance({
-                  ...originalRequest,
-                  headers: { ...originalRequest.headers, Authorization: `Bearer ${refreshedToken}` },
-                });
-              } else {
-                onError?.("Invalid Refresh Token.");
-              }
-
-              setIsRefreshing(false);
+              setIsRefreshing(true);
+              try {
+                const refreshedToken = await debouncedRefresh(refreshUrl);
+                if (refreshedToken) {
+                  // Keep hook state and retry request with new token
+                  setClientAccessToken(refreshedToken);
+                  onSuccess?.();
+                  return http.instance({
+                    ...originalRequest,
+                    headers: { ...(originalRequest.headers ?? {}), Authorization: `Bearer ${refreshedToken}` },
+                  });
+                } else {
+                  onError?.("Invalid Refresh Token.");
+                }
+              } finally {
+                setIsRefreshing(false);
+              }
             }
             return Promise.reject(err);
           })
🧹 Nitpick comments (2)
packages/lib/formatCalendarEvent.ts (2)

17-26: Sanitize only the local-part and handle multiple occurrences of +clientId.

string.replace removes only the first occurrence and may also touch the domain if it ever contained +clientId. Strip from the local-part before the @ and remove all occurrences.

Apply this diff within this block:

-  const team = calEvent.team
-    ? {
-        ...calEvent.team,
-        members: calEvent.team.members.map((member) => ({
-          ...member,
-          email: member.email.replace(`+${clientId}`, ""),
-        })),
-      }
-    : undefined;
+  const team = calEvent.team
+    ? {
+        ...calEvent.team,
+        members: calEvent.team.members.map((member) => ({
+          ...member,
+          email: stripClientIdFromEmail(member.email, clientId),
+        })),
+      }
+    : undefined;

And similarly above in this function for attendees and organizer:

-  const attendees = calEvent.attendees.map((attendee) => ({
+  const attendees = calEvent.attendees.map((attendee) => ({
     ...attendee,
-    email: attendee.email.replace(`+${clientId}`, ""),
+    email: stripClientIdFromEmail(attendee.email, clientId),
   }));
-  const organizer = {
+  const organizer = {
     ...calEvent.organizer,
-    email: calEvent.organizer.email.replace(`+${clientId}`, ""),
+    email: stripClientIdFromEmail(calEvent.organizer.email, clientId),
   };

Helper to add near the top of the file (outside selected range):

// Removes every "+<clientId>" token from the local-part only.
const stripClientIdFromEmail = (email: string, clientId: string) => {
  const at = email.indexOf("@");
  if (at === -1) return email.split(`+${clientId}`).join("");
  const local = email.slice(0, at);
  const domain = email.slice(at + 1);
  const sanitizedLocal = local.split(`+${clientId}`).join("");
  return `${sanitizedLocal}@${domain}`;
};

32-34: Don’t assign team: undefined — preserve semantics of “no team” vs “team present”.

Assigning an explicit undefined may break “presence” checks. Only attach team when it exists.

-    const [attendees, organizer, team] = formatClientIdFromEmails(clonedEvent, clonedEvent.platformClientId);
-    Object.assign(clonedEvent, { attendees, organizer, team });
+    const [attendees, organizer, team] = formatClientIdFromEmails(clonedEvent, clonedEvent.platformClientId);
+    Object.assign(clonedEvent, team ? { attendees, organizer, team } : { attendees, organizer });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e8c2d4f and 3657e3b.

📒 Files selected for processing (2)
  • packages/lib/formatCalendarEvent.ts (1 hunks)
  • packages/platform/atoms/hooks/useOAuthFlow.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/platform/atoms/hooks/useOAuthFlow.ts
  • packages/lib/formatCalendarEvent.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/platform/atoms/hooks/useOAuthFlow.ts
  • packages/lib/formatCalendarEvent.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/platform/atoms/hooks/useOAuthFlow.ts
  • packages/lib/formatCalendarEvent.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-08-27T16:39:38.192Z
Learning: anglerfishlyy successfully implemented CAL-3076 email invitation feature for Cal.com team event-types in PR #23312. The feature allows inviting people via email directly from assignment flow, with automatic team invitation if email doesn't belong to existing team member. Implementation includes Host type modifications (userId?: number, email?: string, isPending?: boolean), CheckedTeamSelect component updates with CreatableSelect, TRPC schema validation with zod email validation, and integration with existing teamInvite system.
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts:118-143
Timestamp: 2025-08-08T09:29:11.681Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (TypeScript), the AIPhoneServiceAgentListItem is required to include user.email in listAgents responses (per maintainer Udit-takkar). Future reviews should not flag this as unnecessary PII unless requirements change.
📚 Learning: 2025-08-27T13:32:46.887Z
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.

Applied to files:

  • packages/lib/formatCalendarEvent.ts
📚 Learning: 2025-08-27T16:39:38.192Z
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-08-27T16:39:38.192Z
Learning: anglerfishlyy successfully implemented CAL-3076 email invitation feature for Cal.com team event-types in PR #23312. The feature allows inviting people via email directly from assignment flow, with automatic team invitation if email doesn't belong to existing team member. Implementation includes Host type modifications (userId?: number, email?: string, isPending?: boolean), CheckedTeamSelect component updates with CreatableSelect, TRPC schema validation with zod email validation, and integration with existing teamInvite system.

Applied to files:

  • packages/lib/formatCalendarEvent.ts
🧬 Code graph analysis (1)
packages/lib/formatCalendarEvent.ts (1)
packages/types/Calendar.d.ts (1)
  • CalendarEvent (163-226)
🔇 Additional comments (2)
packages/platform/atoms/hooks/useOAuthFlow.ts (2)

45-46: Approve: rejecting AxiosError (err) is safe — downstream handlers expect AxiosError, not AxiosResponse.

Search found catch handlers typed as (err: AxiosError) that check err.response?.status and no occurrences of direct error.status or catch parameters typed as AxiosResponse (see packages/platform/atoms/hooks/useOAuthClient.ts ~48–51 and useOAuthFlow.ts ~27–31, ~61–65).


27-45: Resolved — refreshTokens already updates the global Authorization header.
http.refreshTokens calls http.setAuthorizationHeader(res.accessToken) (packages/platform/atoms/lib/http.ts), so adding http.setAuthorizationHeader(refreshedToken) in the interceptor is redundant.

Comment on lines +32 to 34
const [attendees, organizer, team] = formatClientIdFromEmails(clonedEvent, clonedEvent.platformClientId);
Object.assign(clonedEvent, { attendees, organizer, team });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Parity: also sanitize team on ExtendedCalendarEvent.

The hook now sanitizes team.members for CalendarEvent but not for ExtendedCalendarEvent, risking leaks of +clientId in flows using the extended shape (e.g., reminder schedulers).

Proposed change below (not in this hunk) for Lines 41-44:

-  if (clonedEvent.platformClientId) {
-    const [attendees, organizer] = formatClientIdFromEmails(clonedEvent, clonedEvent.platformClientId);
-    Object.assign(clonedEvent, { attendees, organizer });
-  }
+  if (clonedEvent.platformClientId) {
+    const [attendees, organizer, team] = formatClientIdFromEmails(clonedEvent, clonedEvent.platformClientId);
+    Object.assign(clonedEvent, team ? { attendees, organizer, team } : { attendees, organizer });
+  }
📝 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 [attendees, organizer, team] = formatClientIdFromEmails(clonedEvent, clonedEvent.platformClientId);
Object.assign(clonedEvent, { attendees, organizer, team });
}
if (clonedEvent.platformClientId) {
const [attendees, organizer, team] = formatClientIdFromEmails(clonedEvent, clonedEvent.platformClientId);
Object.assign(clonedEvent, team ? { attendees, organizer, team } : { attendees, organizer });
}
🤖 Prompt for AI Agents
In packages/lib/formatCalendarEvent.ts around lines 32 and 41-44, the code
sanitizes team.members for CalendarEvent but not for ExtendedCalendarEvent;
update the ExtendedCalendarEvent handling to apply the same
formatClientIdFromEmails transformation so team.members are sanitized too.
Specifically, when constructing the extended shape, call
formatClientIdFromEmails with the cloned extended event and its platformClientId
(or reuse the same sanitization used for CalendarEvent) and assign the returned
attendees, organizer, and team onto the cloned extended event so team.members no
longer contain +clientId values.

Copy link
Contributor

E2E results are ready!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working core area: core, team members only platform Anything related to our platform plan ready-for-e2e size/S teams area: teams, round robin, collective, managed event-types
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants