Skip to content

fix: Consistently remove a member from organization/sub-team/team - through all the APIs and webapp actions #22806

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

hariombalhara
Copy link
Member

@hariombalhara hariombalhara commented Jul 30, 2025

What does this PR do?

This PR fixes the organization membership deletion functionality in API V2 by properly calling the TeamService.removeMembers method instead of directly deleting the membership record. This ensures that all necessary cleanup operations are performed when removing a user from an organization.

Fixes PRI-297

Bug Fixes

  • When setting organizationId=null, we explicitly make username unique so that slug constraint DB error is avoided
  • ChildrenManaged Events weren't deleted in case of team membership deletion, that is now fixed
  • Even though there are multiple tables need to be cleaned up when deleting a membership from org, that is being done in an atomic(mostly) manner now.

Technical Details:

  • The previous implementation was directly deleting memberships which could leave orphaned data
  • Now uses TeamService.removeMembers({ teamIds: [organizationId], userIds: [membership.userId], isOrg: true })
  • Maintains backward compatibility while ensuring proper cleanup

Visual Demo (For contributors especially)

N/A - This is a backend API change with no visual components.

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. N/A - This is an internal API fix that doesn't change the public API interface.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

Prerequisites:

  • Set up a local Cal.com development environment
  • Ensure you have API V2 enabled
  • Create a test organization with multiple members

Testing Steps:

  • Membership deletion should return 200 with the deleted membership data
  • All related data (hosts, permissions, etc.) should be properly cleaned up
  • Attempting to delete a non-existent membership returns 404
  • Non-admin users should receive 403 when attempting to delete memberships

Checklist

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

Copy link
Contributor

coderabbitai bot commented Jul 30, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This change refactors the logic for removing users from teams and organizations across the codebase. The main removal logic is consolidated within a new TeamService.removeMembers method, which handles deletion of memberships, associated event types, event hosts, profiles, and organization redirects. The previous removeMember function is removed, and all usages are updated to use the new service. Controller and service methods in API v2 are updated to use TeamService.removeMembers for membership deletions. Associated test fixtures are extended with new helper methods, and a comprehensive integration test suite for TeamService.removeMembers is added. Unused or redundant code and tests are deleted accordingly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Sub Team memberships aren't removed (PRI-297)
Sub Team's Events hosts aren't removed for the user (PRI-297)
Sub Team's Children Managed Events for the user aren't removed also (PRI-297)
Profile isn't removed (PRI-297)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Addition of findById methods in test fixtures (apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts, membership.repository.fixture.ts) These additions are general test fixture improvements and not directly required for the linked issue.
Removal of logging from removeMember.handler.ts Logging removal is not directly related to the objectives of PRI-297.
Update of @calcom/platform-libraries version in apps/api/v2/package.json Dependency version bump is not directly tied to the objectives in PRI-297.
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-api-v2-org-user-membership-deletion

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

github-actions bot commented Jul 30, 2025

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Fix membership deletion handling and add tests". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@hariombalhara hariombalhara self-assigned this Jul 30, 2025
@keithwillcode keithwillcode added core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO labels Jul 30, 2025
Copy link

linear bot commented Jul 30, 2025

PRI-297

Copy link

vercel bot commented Jul 30, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
cal ⬜️ Ignored (Inspect) Aug 5, 2025 4:32am
cal-eu ⬜️ Ignored (Inspect) Aug 5, 2025 4:32am

Copy link
Contributor

github-actions bot commented Jul 30, 2025

E2E results are ready!

@hariombalhara hariombalhara force-pushed the fix-api-v2-org-user-membership-deletion branch from 8069a94 to 262ffe4 Compare July 30, 2025 09:19
Copy link

socket-security bot commented Jul 30, 2025

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

…ic methods

- Moved removeMember and all related helper functions from separate file into TeamService class
- Made all functions private static methods instead of exporting them
- Deleted the original removeMember.ts file since it's no longer needed
- Updated imports to use the new location
- All integration tests pass successfully
- Renamed memberId parameter to userId in removeMember private method
- Renamed memberIds parameter to userIds in removeMembers public method
- Updated all calls to removeMembers to use userIds instead of memberIds
- Parameter names now accurately reflect that they are user IDs, not membership IDs
…letion

- Add unit tests for membership deletion services
- Remove redundant deletion behavior tests from controllers
- Keep only happy path tests in e2e controller tests
- Fix unused imports and variables
@hariombalhara hariombalhara force-pushed the fix-api-v2-org-user-membership-deletion branch from baf51ea to 3fef285 Compare July 31, 2025 07:52
@hariombalhara hariombalhara requested review from a team as code owners July 31, 2025 10:32
@graphite-app graphite-app bot requested a review from a team July 31, 2025 10:32
@dosubot dosubot bot added api area: API, enterprise API, access token, OAuth organizations area: organizations, orgs platform Anything related to our platform plan teams area: teams, round robin, collective, managed event-types labels Jul 31, 2025
Copy link

graphite-app bot commented Jul 31, 2025

Graphite Automations

"Add consumer team as reviewer" took an action on this PR • (07/31/25)

1 reviewer was added to this PR based on Keith Williams's automation.

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

🔭 Outside diff range comments (2)
apps/api/v2/src/modules/teams/memberships/controllers/teams-memberships.controller.ts (1)

112-137: Fix duplicate membership updates and incorrect return value

The method updates the membership twice with the same parameters and returns the result from the first update instead of the second. This appears to be unintentional code duplication.

Apply this diff to fix the logic:

  async updateTeamMembership(
    @Param("teamId", ParseIntPipe) teamId: number,
    @Param("membershipId", ParseIntPipe) membershipId: number,
    @Body() body: UpdateTeamMembershipInput
  ): Promise<UpdateTeamMembershipOutput> {
-    const membership = await this.teamsMembershipsService.updateTeamMembership(teamId, membershipId, body);
-
    const currentMembership = await this.teamsMembershipsService.getTeamMembership(teamId, membershipId);

    const updatedMembership = await this.teamsMembershipsService.updateTeamMembership(
      teamId,
      membershipId,
      body
    );
    if (!currentMembership.accepted && updatedMembership.accepted) {
      try {
        await updateNewTeamMemberEventTypes(updatedMembership.userId, teamId);
      } catch (err) {
        this.logger.error("Could not update new team member eventTypes", err);
      }
    }
    return {
      status: SUCCESS_STATUS,
-      data: plainToClass(TeamMembershipOutput, membership, { strategy: "excludeAll" }),
+      data: plainToClass(TeamMembershipOutput, updatedMembership, { strategy: "excludeAll" }),
    };
  }
packages/lib/server/service/teamService.ts (1)

84-112: Consider error handling strategy for batch removals

The current implementation uses Promise.all for member removals, which will reject entirely if any single removal fails. This could leave the operation in a partially completed state.

Consider using Promise.allSettled to handle failures gracefully and provide detailed results:

  static async removeMembers({
    teamIds,
    userIds,
    isOrg = false,
  }: {
    teamIds: number[];
    userIds: number[];
    isOrg?: boolean;
  }) {
    const deleteMembershipPromises: Promise<RemoveMemberResult>[] = [];

    for (const userId of userIds) {
      for (const teamId of teamIds) {
        deleteMembershipPromises.push(
          TeamService.removeMember({
            teamId,
            userId,
            isOrg,
          })
        );
      }
    }

-    await Promise.all(deleteMembershipPromises);
+    const results = await Promise.allSettled(deleteMembershipPromises);
+    
+    // Log failures but continue with billing updates
+    const failures = results.filter((r) => r.status === 'rejected');
+    if (failures.length > 0) {
+      log.error('Some membership removals failed', { failures });
+    }

    const teamsBilling = await TeamBilling.findAndInitMany(teamIds);
    const teamBillingPromises = teamsBilling.map((teamBilling) => teamBilling.updateQuantity());
    await Promise.allSettled(teamBillingPromises);
+    
+    // Optionally, throw an aggregate error if any removals failed
+    if (failures.length > 0) {
+      throw new Error(`Failed to remove ${failures.length} membership(s)`);
+    }
  }
🧹 Nitpick comments (4)
packages/platform/libraries/package.json (1)

3-3: Consider using semantic versioning instead of 9.9.9.

The version jump from "0.0.0" to "9.9.9" is unusual and doesn't follow typical semantic versioning practices. For a significant refactor introducing new TeamService functionality, consider using a more appropriate version like "1.0.0" (major version for breaking changes) or "0.1.0" (minor version if maintaining pre-1.0 status).

-  "version": "9.9.9",
+  "version": "1.0.0",
apps/api/v2/package.json (1)

41-41: Dependency version update aligns with package changes.

The update to "@calcom/platform-libraries@9.9.9" is consistent with the version bump in the libraries package. However, consider using proper semantic versioning (e.g., "1.0.0") instead of "9.9.9" for better version management practices.

packages/lib/server/service/teamService.ts (1)

195-270: Consider implementing the TODO: Move fetch methods to repositories

The TODO comments correctly identify that these fetch methods should be moved to their respective repository classes for better separation of concerns and adherence to the repository pattern.

Consider creating:

  • MembershipRepository for fetchMembershipOrThrow
  • Update existing TeamRepository for fetchTeamOrThrow
  • UserRepository for fetchUserOrThrow

This would improve code organization and make the service layer focus on business logic rather than data access.

packages/lib/server/service/__tests__/teamService.integration-test.ts (1)

174-178: Simplify the membership deletion query.

The current OR mapping can be simplified.

-  await prisma.membership.deleteMany({
-    where: {
-      OR: teamIds.map((teamId) => ({ teamId })),
-    },
-  });
+  await prisma.membership.deleteMany({
+    where: {
+      teamId: { in: teamIds },
+    },
+  });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3601a9a and 0d1c0f9.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (17)
  • apps/api/v2/package.json (1 hunks)
  • apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/teams/memberships/organizations-teams-memberships.controller.ts (0 hunks)
  • apps/api/v2/src/modules/organizations/teams/memberships/services/organizations-teams-memberships.service.ts (2 hunks)
  • apps/api/v2/src/modules/teams/event-types/services/teams-event-types.service.ts (0 hunks)
  • apps/api/v2/src/modules/teams/memberships/controllers/teams-memberships.controller.ts (1 hunks)
  • apps/api/v2/src/modules/teams/memberships/services/teams-memberships.service.ts (2 hunks)
  • apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts (1 hunks)
  • apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts (1 hunks)
  • packages/features/ee/dsync/lib/removeUserFromOrg.ts (1 hunks)
  • packages/features/ee/teams/lib/removeMember.ts (0 hunks)
  • packages/lib/server/service/__tests__/teamService.integration-test.ts (1 hunks)
  • packages/lib/server/service/teamService.ts (3 hunks)
  • packages/platform/libraries/index.ts (1 hunks)
  • packages/platform/libraries/package.json (1 hunks)
  • packages/trpc/server/routers/viewer/teams/removeMember.handler.ts (1 hunks)
  • packages/trpc/server/routers/viewer/teams/removeMember.test.ts (0 hunks)
💤 Files with no reviewable changes (4)
  • apps/api/v2/src/modules/teams/event-types/services/teams-event-types.service.ts
  • packages/features/ee/teams/lib/removeMember.ts
  • apps/api/v2/src/modules/organizations/teams/memberships/organizations-teams-memberships.controller.ts
  • packages/trpc/server/routers/viewer/teams/removeMember.test.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

Files:

  • apps/api/v2/src/modules/teams/memberships/services/teams-memberships.service.ts
  • apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts
  • apps/api/v2/src/modules/organizations/teams/memberships/services/organizations-teams-memberships.service.ts
**/*.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:

  • apps/api/v2/src/modules/teams/memberships/services/teams-memberships.service.ts
  • apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts
  • apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts
  • packages/platform/libraries/index.ts
  • packages/trpc/server/routers/viewer/teams/removeMember.handler.ts
  • apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts
  • packages/features/ee/dsync/lib/removeUserFromOrg.ts
  • apps/api/v2/src/modules/teams/memberships/controllers/teams-memberships.controller.ts
  • apps/api/v2/src/modules/organizations/teams/memberships/services/organizations-teams-memberships.service.ts
  • packages/lib/server/service/__tests__/teamService.integration-test.ts
  • packages/lib/server/service/teamService.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:

  • apps/api/v2/src/modules/teams/memberships/services/teams-memberships.service.ts
  • apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts
  • apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts
  • packages/platform/libraries/index.ts
  • packages/trpc/server/routers/viewer/teams/removeMember.handler.ts
  • apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts
  • packages/features/ee/dsync/lib/removeUserFromOrg.ts
  • apps/api/v2/src/modules/teams/memberships/controllers/teams-memberships.controller.ts
  • apps/api/v2/src/modules/organizations/teams/memberships/services/organizations-teams-memberships.service.ts
  • packages/lib/server/service/__tests__/teamService.integration-test.ts
  • packages/lib/server/service/teamService.ts
**/*Service.ts

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

Files:

  • packages/lib/server/service/teamService.ts
🧠 Learnings (6)
apps/api/v2/src/modules/teams/memberships/services/teams-memberships.service.ts (1)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*Service.ts : Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

packages/platform/libraries/index.ts (1)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*Service.ts : Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts (1)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*Service.ts : Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

apps/api/v2/src/modules/organizations/teams/memberships/services/organizations-teams-memberships.service.ts (2)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*Service.ts : Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*Repository.ts : Repository files must include Repository suffix, prefix with technology if applicable (e.g., PrismaAppRepository.ts), and use PascalCase matching the exported class

packages/lib/server/service/__tests__/teamService.integration-test.ts (2)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*Service.ts : Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*.{service,repository}.ts : Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

packages/lib/server/service/teamService.ts (1)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.946Z
Learning: Applies to **/*Service.ts : Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

🧬 Code Graph Analysis (6)
apps/api/v2/src/modules/teams/memberships/services/teams-memberships.service.ts (1)
packages/lib/server/service/teamService.ts (1)
  • TeamService (50-381)
apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts (1)
packages/lib/event-types/getEventTypeById.ts (1)
  • EventType (34-34)
packages/trpc/server/routers/viewer/teams/removeMember.handler.ts (1)
packages/lib/server/service/teamService.ts (1)
  • TeamService (50-381)
apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts (1)
packages/lib/server/service/teamService.ts (1)
  • TeamService (50-381)
apps/api/v2/src/modules/organizations/teams/memberships/services/organizations-teams-memberships.service.ts (1)
packages/lib/server/service/teamService.ts (1)
  • TeamService (50-381)
packages/lib/server/service/__tests__/teamService.integration-test.ts (1)
packages/lib/server/service/teamService.ts (1)
  • TeamService (50-381)
🔇 Additional comments (16)
packages/platform/libraries/index.ts (1)

122-123: LGTM! Proper export of the centralized TeamService.

The addition of TeamService export aligns with the PR objective to centralize team membership removal logic. The export is correctly placed and follows the established patterns in this file.

packages/trpc/server/routers/viewer/teams/removeMember.handler.ts (1)

62-62: LGTM! Improved API design with named parameters.

The change from positional arguments to a named parameter object improves the API's maintainability and reduces the chance of parameter ordering errors. The parameter mapping from memberIds to userIds aligns with the new TeamService.removeMembers signature.

apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts (1)

63-65: LGTM! Useful addition for test scenarios.

The new findById method follows established patterns in the fixture class, correctly uses the read client for query operations, and maintains consistency with the parameter typing used in the delete method. This method will be valuable for testing event type retrieval scenarios, especially given the enhanced event type cleanup logic in the new TeamService.removeMembers implementation.

apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts (1)

39-41: LGTM! Good addition for testing the refactored services.

The new findById method correctly uses findUnique for ID-based lookups, which is more semantically appropriate and potentially more efficient than findFirst. This supports the testing needs for the refactored deletion services.

apps/api/v2/src/modules/teams/memberships/services/teams-memberships.service.ts (2)

6-6: LGTM! Proper import for the refactored TeamService.

The import correctly brings in TeamService from @calcom/platform-libraries to support the centralized member removal functionality.


45-56: Excellent refactor for proper cleanup and error handling.

The updated deleteTeamMembership method now:

  • Fetches the membership first to ensure it exists and get the userId
  • Provides clear error messaging if membership not found
  • Delegates to TeamService.removeMembers for comprehensive cleanup of related data
  • Uses isOrg: false appropriately for team (not organization) memberships

This ensures atomic cleanup of memberships, event types, hosts, and other related data, addressing the core issue described in the PR objectives.

apps/api/v2/src/modules/organizations/teams/memberships/services/organizations-teams-memberships.service.ts (2)

6-6: LGTM! Consistent import pattern.

The import correctly adds TeamService from @calcom/platform-libraries for the centralized member removal functionality.


62-79: Excellent refactor with improved error messaging.

The updated deleteOrgTeamMembership method follows the same pattern as other services:

  • Fetches membership first to validate existence and get userId
  • Provides detailed error message including all relevant IDs for better debugging
  • Uses TeamService.removeMembers with isOrg: false (correct since this removes from a team, not from the organization itself)
  • Ensures comprehensive cleanup of related data

The approach is consistent with the other membership services and addresses the cleanup requirements outlined in the PR objectives.

apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts (2)

5-5: LGTM! Consistent import for centralized member removal.

The import correctly adds TeamService from @calcom/platform-libraries to support the refactored deletion logic.


66-86: Excellent refactor with correct organization-level removal.

The updated deleteOrgMembership method:

  • Fetches membership first to validate existence and get userId
  • Provides clear error messaging with relevant IDs
  • Correctly uses isOrg: true since this removes a user from the entire organization
  • Delegates to TeamService.removeMembers for comprehensive cleanup
  • Maintains the existing API contract by returning the formatted membership output

This ensures proper cleanup of all organization-related data while maintaining backward compatibility.

packages/features/ee/dsync/lib/removeUserFromOrg.ts (2)

1-1: LGTM! Direct import for TeamService.

The import uses the direct path to TeamService which is appropriate for this package context.


4-4: Excellent simplification with proper organization removal.

The refactored function correctly:

  • Uses TeamService.removeMembers for centralized cleanup logic
  • Sets isOrg: true appropriately for organization-level removal
  • Maintains the same function signature for backward compatibility
  • Simplifies the implementation while ensuring comprehensive data cleanup

This aligns with the broader refactor to centralize member removal logic in TeamService.

packages/lib/server/service/__tests__/teamService.integration-test.ts (4)

22-116: Well-structured test helper functions!

The helper functions are properly designed with unique identifiers using timestamps and random suffixes to prevent conflicts during parallel test execution.


144-147: Good adherence to Prisma query guidelines!

Excellent use of select to fetch only the required organizationId field, following the coding guideline to avoid using include.


636-707: Excellent transaction rollback test!

This test comprehensively verifies transactional integrity by intentionally causing a username conflict and ensuring all operations are rolled back. Great coverage of all affected entities.


818-884: Comprehensive test for the PR's main fix!

This test perfectly captures the API v2 deletion scenario that the PR aims to fix, including:

  • Username conflict resolution by appending userId
  • Profile deletion
  • Host removal from sub-teams
  • Complete membership cleanup

Great job ensuring the fix works as intended.

The E2E API v2 tests were failing with TypeScript compilation errors because
TeamService imports from @calcom/platform-libraries require the package to be
built first to generate the dist/ folder with compiled exports.

This adds the same build step that was added to unit-tests.yml to resolve
the module resolution errors.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
@hariombalhara
Copy link
Member Author

The unit and E2e Tests are failing because platform-libraries aren't published. Ran a workflow where platform-loibraries were built and then tests were run and it passed.
https://github.com/calcom/cal.com/actions/runs/16670846513

@hariombalhara hariombalhara force-pushed the fix-api-v2-org-user-membership-deletion branch from 879194f to 7b50b3e Compare August 4, 2025 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api area: API, enterprise API, access token, OAuth core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO organizations area: organizations, orgs platform Anything related to our platform plan ready-for-e2e teams area: teams, round robin, collective, managed event-types
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants