Skip to content

Conversation

AyushMukkanwar
Copy link
Contributor

@AyushMukkanwar AyushMukkanwar commented Oct 12, 2025

What does this PR do?

This PR refactors the user creation logic within the team member invitation flow, as requested by the issue. It replaces a direct prisma.user.create call with a new, safer UserRepository.createFromInvitation method.

This change centralizes the business logic for creating a new user (including the conditional creation of default schedules and profiles) within the repository. This improves maintainability and adheres to the project's established repository pattern. An integration test was also added to verify the behavior is correct.

Fixes #14869
Fixes CAL-3632
Visual Demo (For contributors especially)

N/A - This is a backend refactoring and has no visual changes.

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 (https://cal.com/docs). N/A
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?
The new integration test added in packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.test.ts specifically covers the new user creation path and passes.

Manual testing:
As a team owner, invite a brand new email address that has never been used on the platform.
Confirm that the invitation is sent.
Check the database to ensure the new User record has been created with a pending Membership for that team.

Copy link

vercel bot commented Oct 12, 2025

@AyushMukkanwar is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Oct 12, 2025
@graphite-app graphite-app bot requested a review from a team October 12, 2025 13:06
@github-actions github-actions bot added Low priority Created by Linear-GitHub Sync ⚡ Quick Wins A collection of quick wins/quick fixes that are less than 30 minutes of work 💻 refactor labels Oct 12, 2025
Copy link
Contributor

coderabbitai bot commented Oct 12, 2025

Walkthrough

Constructor for UserRepository now accepts PrismaClient | PrismaTransaction. Added UserRepository.createFromInvitation(...) which centralizes creating a user from invitation data (conditionally creates profile and default schedule, resolves translations, computes default availability, and performs a single nested user.create call). inviteMember utils now use UserRepository.createFromInvitation instead of in-file tx.user.create and removed local schedule/profile construction logic. Integration tests updated: removed a helper, adjusted cleanup catch, and added a test asserting inviting a non-existent email creates a new user and a pending membership.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly describes the refactoring of the team invitation flow to use the UserRepository for new user invitations and aligns with the main change in the pull request.
Linked Issues Check ✅ Passed The implementation replaces direct prisma user creation with the repository method as mandated by issues #14869 and CAL-3632, thereby centralizing user creation logic within the repository.
Out of Scope Changes Check ✅ Passed All modifications are focused on refactoring the invitation path to use UserRepository.createFromInvitation and related tests, with no unrelated changes introduced.
Description Check ✅ Passed The description accurately details the replacement of direct prisma.user.create calls with UserRepository.createFromInvitation in the team member invitation flow, centralization of business logic, and inclusion of integration tests.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@dosubot dosubot bot added the teams area: teams, round robin, collective, managed event-types label Oct 12, 2025
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: 0

🧹 Nitpick comments (1)
packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts (1)

340-356: Drop the double cast when wiring the repository.

Inside interactive transactions we get a Prisma.TransactionClient, and force-casting it to PrismaClient hides the mismatch—if UserRepository ever calls a PrismaClient-only helper (e.g. $transaction, $connect), we’ll hit a runtime failure. Prefer widening the repository constructor to accept a TransactionClient and pass tx straight through; the cast and extra import then disappear.

-        const userRepository = new UserRepository(tx as unknown as PrismaClient);
+        const userRepository = new UserRepository(tx);
-  constructor(private prismaClient: PrismaClient) {}
+  constructor(private prismaClient: PrismaClient | Prisma.TransactionClient) {}
📜 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 eae2711 and 42bf80c.

📒 Files selected for processing (3)
  • packages/lib/server/repository/user.ts (3 hunks)
  • packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.integration-test.ts (4 hunks)
  • packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts (3 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/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.integration-test.ts
  • packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts
  • packages/lib/server/repository/user.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/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.integration-test.ts
  • packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts
  • packages/lib/server/repository/user.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/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.integration-test.ts
  • packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts
  • packages/lib/server/repository/user.ts
🧠 Learnings (1)
📓 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: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-09-16T08:59:45.884Z
Learning: anglerfishlyy's PR #23467 demonstrates excellent software architecture with proper email validation (regex + zod), secure token generation using randomBytes, comprehensive host type system using discriminated unions (userId OR email), and smart reuse of existing team invitation infrastructure. The implementation includes robust error handling, type safety, and maintains backward compatibility while adding CreatableSelect-based email invitation functionality.
🧬 Code graph analysis (3)
packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.integration-test.ts (2)
packages/platform/libraries/index.ts (1)
  • MembershipRole (32-32)
packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts (1)
  • inviteMemberHandler (297-303)
packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts (2)
packages/lib/server/repository/user.ts (1)
  • UserRepository (126-1153)
packages/prisma/index.ts (1)
  • PrismaClient (84-84)
packages/lib/server/repository/user.ts (2)
packages/platform/libraries/index.ts (1)
  • CreationSource (28-28)
packages/lib/server/repository/profile.ts (1)
  • ProfileRepository (67-726)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests / Unit
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Install dependencies / Yarn install & cache

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

🧹 Nitpick comments (1)
packages/lib/server/repository/user.ts (1)

717-762: Consider adding a select clause to limit returned fields.

The create operation returns all user fields. As per coding guidelines, "only select data you need" for Prisma queries. While create operations may legitimately return the full record, consider whether all fields are needed by callers or if a more focused selection would be appropriate.

As per coding guidelines.

📜 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 42bf80c and 861b0fb.

📒 Files selected for processing (1)
  • packages/lib/server/repository/user.ts (2 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/lib/server/repository/user.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/lib/server/repository/user.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/lib/server/repository/user.ts
🧠 Learnings (1)
📓 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: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-09-16T08:59:45.884Z
Learning: anglerfishlyy's PR #23467 demonstrates excellent software architecture with proper email validation (regex + zod), secure token generation using randomBytes, comprehensive host type system using discriminated unions (userId OR email), and smart reuse of existing team invitation infrastructure. The implementation includes robust error handling, type safety, and maintains backward compatibility while adding CreatableSelect-based email invitation functionality.
🧬 Code graph analysis (1)
packages/lib/server/repository/user.ts (1)
packages/lib/server/repository/profile.ts (1)
  • ProfileRepository (67-726)
🔇 Additional comments (1)
packages/lib/server/repository/user.ts (1)

688-765: Well-structured invitation flow with clear conditional logic.

The method properly centralizes user creation logic for invitation scenarios, with appropriate conditional handling for:

  • Profile creation when organizationId and username are both provided
  • Default schedule creation for non-platform-managed users

The logging and translation setup are also handled correctly.

@AyushMukkanwar AyushMukkanwar force-pushed the refactor/final-clean-commit branch from 861b0fb to 41f4bce Compare October 12, 2025 14:22
Copy link
Contributor

@Udit-takkar Udit-takkar left a comment

Choose a reason for hiding this comment

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

Please remove all the AI generated comments

@AyushMukkanwar
Copy link
Contributor Author

Please remove all the AI generated comments

My initial commit had no comments but code rabbit insisted on commenting. Should i remove the comments then?

role: invitation.role,
accepted: autoAccept, // If the user is invited to a child team, they are automatically accepted
},
const userRepository = new UserRepository(tx as unknown as PrismaClient);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should try to avoid type assertions maybe we should change the type to PrismaClient | PrismaTransactionClient

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for this feedback, I will make the appropriate change.

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

🧹 Nitpick comments (1)
packages/lib/server/repository/user.ts (1)

688-761: Consider extracting shared user creation logic.

Both create (lines 625-680) and createFromInvitation duplicate logic for:

  • Getting translated schedule names
  • Computing default availability
  • Creating profiles when organizationId exists
  • Creating schedules with identical structure

While some duplication is acceptable for distinct flows, extracting shared helpers (e.g., buildDefaultScheduleData, buildProfileData) could improve maintainability and reduce the risk of drift between the two methods.

📜 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 41f4bce and 9815eda.

📒 Files selected for processing (2)
  • packages/lib/server/repository/user.ts (4 hunks)
  • packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts (3 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/trpc/server/routers/viewer/teams/inviteMember/utils.ts
  • packages/lib/server/repository/user.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/trpc/server/routers/viewer/teams/inviteMember/utils.ts
  • packages/lib/server/repository/user.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/trpc/server/routers/viewer/teams/inviteMember/utils.ts
  • packages/lib/server/repository/user.ts
🧠 Learnings (3)
📚 Learning: 2025-09-09T03:29:43.025Z
Learnt from: emrysal
PR: calcom/cal.com#23692
File: packages/lib/server/service/InsightsBookingBaseService.ts:16-16
Timestamp: 2025-09-09T03:29:43.025Z
Learning: In the Cal.com codebase, readonlyPrisma is still an instance of PrismaClient, making type changes from `typeof readonlyPrisma` to `PrismaClient` less critical since they are fundamentally compatible types.

Applied to files:

  • packages/trpc/server/routers/viewer/teams/inviteMember/utils.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/server/repository/user.ts
📚 Learning: 2025-09-16T08:59:45.884Z
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-09-16T08:59:45.884Z
Learning: anglerfishlyy's PR #23467 demonstrates excellent software architecture with proper email validation (regex + zod), secure token generation using randomBytes, comprehensive host type system using discriminated unions (userId OR email), and smart reuse of existing team invitation infrastructure. The implementation includes robust error handling, type safety, and maintains backward compatibility while adding CreatableSelect-based email invitation functionality.

Applied to files:

  • packages/lib/server/repository/user.ts
🧬 Code graph analysis (2)
packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts (1)
packages/lib/server/repository/user.ts (1)
  • UserRepository (123-1152)
packages/lib/server/repository/user.ts (2)
packages/platform/libraries/index.ts (1)
  • CreationSource (28-28)
packages/lib/server/repository/profile.ts (1)
  • ProfileRepository (67-726)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Linters / lint
  • GitHub Check: Type check / check-types
  • GitHub Check: Tests / Unit
🔇 Additional comments (5)
packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts (1)

340-359: LGTM! Successful refactor to repository pattern.

The refactor successfully centralizes user creation logic into UserRepository.createFromInvitation, improving maintainability and following the repository pattern. The transaction handling is correct, and all required parameters (email, username, language, isPlatformManaged, organizationId, invitedTo, teams) are properly passed.

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

124-124: LGTM: Transaction support added.

Accepting PrismaClient | PrismaTransaction enables using this repository within database transactions, which is essential for atomic multi-step operations like user creation with related records.


688-700: Well-documented method signature.

The method signature clearly defines all required parameters for invitation-based user creation. The JSDoc comment provides helpful context about the method's purpose.


739-756: Schedule creation correctly gated by platform management.

Platform-managed users don't need default schedules since their availability is managed externally. This conditional logic appropriately skips schedule creation for such users.


728-738: Confirm intentional stricter profile creation condition
Profile creation now requires both organizationId and username; in the invitation flow orgMemberUsername and orgId are only set for org invites and both are falsy for regular team members, so this change is intentional.

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: 0

🧹 Nitpick comments (1)
packages/lib/server/repository/user.ts (1)

682-686: Consider enriching JSDoc with parameter descriptions.

The comments explain the method's purpose well. For better maintainability, consider adding @param tags describing each parameter and an @returns tag.

📜 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 2412612 and 6e40362.

📒 Files selected for processing (1)
  • packages/lib/server/repository/user.ts (3 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/lib/server/repository/user.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/lib/server/repository/user.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/lib/server/repository/user.ts
🧠 Learnings (2)
📚 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/server/repository/user.ts
📚 Learning: 2025-09-16T08:59:45.884Z
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-09-16T08:59:45.884Z
Learning: anglerfishlyy's PR #23467 demonstrates excellent software architecture with proper email validation (regex + zod), secure token generation using randomBytes, comprehensive host type system using discriminated unions (userId OR email), and smart reuse of existing team invitation infrastructure. The implementation includes robust error handling, type safety, and maintains backward compatibility while adding CreatableSelect-based email invitation functionality.

Applied to files:

  • packages/lib/server/repository/user.ts
🧬 Code graph analysis (1)
packages/lib/server/repository/user.ts (1)
packages/lib/server/repository/profile.ts (1)
  • ProfileRepository (67-726)
🔇 Additional comments (2)
packages/lib/server/repository/user.ts (2)

124-124: LGTM! Constructor now supports transactions.

Accepting PrismaClient | PrismaTransaction allows the repository to be used within transaction contexts, improving flexibility and enabling atomic operations across multiple repository calls.


688-761: Verify preconditions and password flow for invitation-based user creation

  • In packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts:341, confirm there’s a uniqueness check for invitation.usernameOrEmail before calling createFromInvitation to avoid DB constraint errors.
  • Ensure invited users set their password in the invitation acceptance/signup flow, since createFromInvitation doesn’t handle password assignment.

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

Labels

community Created by Linear-GitHub Sync Low priority Created by Linear-GitHub Sync ⚡ Quick Wins A collection of quick wins/quick fixes that are less than 30 minutes of work 💻 refactor size/L teams area: teams, round robin, collective, managed event-types

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CAL-3632] On signup, use the UserRepository.create method

2 participants