Skip to content

Comments

fix: managed organization bookings#23078

Merged
supalarry merged 3 commits intomainfrom
lauris/cal-6250-fix-managed-organization-bookings
Aug 14, 2025
Merged

fix: managed organization bookings#23078
supalarry merged 3 commits intomainfrom
lauris/cal-6250-fix-managed-organization-bookings

Conversation

@supalarry
Copy link
Contributor

Linear CAL-6250

@supalarry supalarry requested a review from a team as a code owner August 14, 2025 08:46
@supalarry supalarry requested a review from a team August 14, 2025 08:46
@linear
Copy link

linear bot commented Aug 14, 2025

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 14, 2025

Walkthrough

Replaces a locally defined UpdatePrivateLinkBody DTO in the event-types private-links controller with the exported UpdatePrivateLinkBody from @calcom/platform-types and removes OmitType usage. Adds UpdatePrivateLinkBody to platform types with optional expiresAt and maxUsageCount fields and validations. Introduces an E2E test for organization bookings covering manager and managed organizations scenarios and adds a fixture helper to create managed-organization relations. Adjusts bookings retrieval logic to include organization-scoped memberships when user.orgId is present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Scope get handler to passed orgId while returning memberships of both organization and all organization teams (CAL-6250) Code change in packages/trpc/server/routers/viewer/bookings/get.handler.ts uses user.orgId to scope membership lookup rather than the route parameter orgId; does not ensure handler is scoped to the requested orgId.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Replace local UpdatePrivateLinkBody DTO with import from @calcom/platform-types (apps/api/v2/src/ee/event-types-private-links/controllers/event-types-private-links.controller.ts) This concerns private-link DTO/controller changes and is unrelated to bookings/membership scoping in CAL-6250.
Add UpdatePrivateLinkBody type with expiresAt and maxUsageCount (packages/platform/types/event-types/inputs/private-link.input.ts) Adds a new input type unrelated to the bookings/membership scoping objective.
Add fixture method createManagedOrganization (apps/api/v2/test/fixtures/repository/managed-organizations.repository.fixture.ts) Test fixture utility is for E2E setup and does not change the bookings handler behavior required by the issue.

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch lauris/cal-6250-fix-managed-organization-bookings

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.
    • 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.
  • 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@graphite-app graphite-app bot requested a review from a team August 14, 2025 08:46
@keithwillcode keithwillcode added core area: core, team members only platform Anything related to our platform plan labels Aug 14, 2025
@graphite-app
Copy link

graphite-app bot commented Aug 14, 2025

Graphite Automations

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

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

"Add ready-for-e2e label" took an action on this PR • (08/14/25)

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

@dosubot dosubot bot added bookings area: bookings, availability, timezones, double booking organizations area: organizations, orgs 🐛 bug Something isn't working labels Aug 14, 2025
@vercel
Copy link

vercel bot commented Aug 14, 2025

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

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal ⬜️ Ignored Aug 14, 2025 8:54am
cal-eu ⬜️ Ignored Aug 14, 2025 8:54am

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 (5)
apps/api/v2/test/fixtures/repository/managed-organizations.repository.fixture.ts (1)

38-45: LGTM: Useful fixture for seeding managed org relationships

The helper is straightforward and does exactly what's needed for tests.

Minor nitpick: to align with the "select-only what you need" guideline (even in tests), you can restrict the returned fields.

-  async createManagedOrganization(managerOrganizationId: number, managedOrganizationId: number) {
-    return this.prismaWriteClient.managedOrganization.create({
-      data: {
-        managerOrganizationId,
-        managedOrganizationId,
-      },
-    });
-  }
+  async createManagedOrganization(managerOrganizationId: number, managedOrganizationId: number) {
+    return this.prismaWriteClient.managedOrganization.create({
+      data: {
+        managerOrganizationId,
+        managedOrganizationId,
+      },
+      select: {
+        managerOrganizationId: true,
+        managedOrganizationId: true,
+      },
+    });
+  }
packages/platform/types/event-types/inputs/private-link.input.ts (1)

62-83: Centralizing UpdatePrivateLinkBody in platform-types is the right move

The exported DTO with validations is clean and reusable. One small Swagger consistency nit: the example shows an ISO string; consider annotating the type as String for better OpenAPI consumers compatibility (same as CreatePrivateLinkInput).

   @ApiPropertyOptional({
-    description: "New expiration date for time-based links",
-    type: Date,
+    description: "New expiration date for time-based links",
+    type: String,
     example: "2024-12-31T23:59:59.000Z",
   })
apps/api/v2/src/modules/organizations/bookings/managed-organizations-bookings.controller.e2e-spec.ts (3)

481-499: Ensure OAuth headers are consistently provided (test parity and determinism)

In the first and third tests you pass X_CAL_CLIENT_ID and X_CAL_SECRET_KEY; here you don’t. If the endpoint requires client credentials in real deployments, this can hide issues and reduce test fidelity. Recommend adding them for consistency.

       return request(app.getHttpServer())
         .get(`/v2/organizations/${managerOrganization.id}/bookings?userIds=${managerOrgUser2.id}`)
         .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
+        .set(X_CAL_CLIENT_ID, oAuthClient.id)
+        .set(X_CAL_SECRET_KEY, oAuthClient.secret)
         .expect(200)

541-549: Tighten teardown to avoid cross-test leakage

Cleanup misses several entities created during setup (managed org, the second org user, managed org user, and their bookings/event types). This can cause flakiness when tests share DB state.

Consider extending afterAll:

     afterAll(async () => {
       await oauthClientRepositoryFixture.delete(oAuthClient.id);
-      await teamRepositoryFixture.delete(managerOrganization.id);
+      await teamRepositoryFixture.delete(managerOrganization.id);
+      await teamRepositoryFixture.delete(managedOrganization.id);
       await userRepositoryFixture.deleteByEmail(managerOrgUser1.email);
+      await userRepositoryFixture.deleteByEmail(managerOrgUser2.email);
+      await userRepositoryFixture.deleteByEmail(managedOrgUser.email);
       await userRepositoryFixture.deleteByEmail(nonOrgUser1.email);
       await bookingsRepositoryFixture.deleteAllBookings(managerOrgUser1.id, managerOrgUser1.email);
+      await bookingsRepositoryFixture.deleteAllBookings(managerOrgUser2.id, managerOrgUser2.email);
+      await bookingsRepositoryFixture.deleteAllBookings(managedOrgUser.id, managedOrgUser.email);
       await bookingsRepositoryFixture.deleteAllBookings(nonOrgUser1.id, nonOrgUser1.email);
+      // Optional: remove event types to ensure no orphaned data if cascading is not configured
+      // await eventTypesRepositoryFixture.delete(managerOrgEventTypeId);
+      // await eventTypesRepositoryFixture.delete(nonOrgEventTypeId);
+      // await eventTypesRepositoryFixture.delete(managedOrgEventTypeId);
       await app.close();
     });

322-325: Good: explicit seeding of managed-organization relation

This ensures the manager→managed linkage exists for the test scenarios that validate scoping. Consider asserting the relation exists (via fixture.getManagedOrganization) to fail fast if seeding breaks in future refactors.

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2d5a867 and 7ef4bef.

📒 Files selected for processing (5)
  • apps/api/v2/src/ee/event-types-private-links/controllers/event-types-private-links.controller.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/bookings/managed-organizations-bookings.controller.e2e-spec.ts (1 hunks)
  • apps/api/v2/test/fixtures/repository/managed-organizations.repository.fixture.ts (1 hunks)
  • packages/platform/types/event-types/inputs/private-link.input.ts (2 hunks)
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.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/organizations/bookings/managed-organizations-bookings.controller.e2e-spec.ts
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • apps/api/v2/test/fixtures/repository/managed-organizations.repository.fixture.ts
  • packages/platform/types/event-types/inputs/private-link.input.ts
  • apps/api/v2/src/ee/event-types-private-links/controllers/event-types-private-links.controller.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/organizations/bookings/managed-organizations-bookings.controller.e2e-spec.ts
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • apps/api/v2/test/fixtures/repository/managed-organizations.repository.fixture.ts
  • packages/platform/types/event-types/inputs/private-link.input.ts
  • apps/api/v2/src/ee/event-types-private-links/controllers/event-types-private-links.controller.ts
🧠 Learnings (2)
📚 Learning: 2025-07-16T05:10:22.891Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#22304
File: packages/prisma/schema.prisma:1068-1071
Timestamp: 2025-07-16T05:10:22.891Z
Learning: In PR #22304 for Cal.com private link expiration features, the `maxUsageCount` field was intentionally set to default to 1 (non-nullable) as a breaking change, making all existing private links single-use after migration. This was a deliberate design decision by alishaz-polymath.

Applied to files:

  • packages/platform/types/event-types/inputs/private-link.input.ts
📚 Learning: 2025-07-16T06:42:27.024Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#22304
File: packages/features/eventtypes/components/MultiplePrivateLinksController.tsx:92-94
Timestamp: 2025-07-16T06:42:27.024Z
Learning: In the MultiplePrivateLinksController component (packages/features/eventtypes/components/MultiplePrivateLinksController.tsx), the `currentLink.maxUsageCount ?? 1` fallback in the openSettingsDialog function is intentional. Missing maxUsageCount values indicate old/legacy private links that existed before the expiration feature was added, and they should default to single-use behavior (1) for backward compatibility.

Applied to files:

  • packages/platform/types/event-types/inputs/private-link.input.ts
🧬 Code Graph Analysis (1)
apps/api/v2/src/modules/organizations/bookings/managed-organizations-bookings.controller.e2e-spec.ts (11)
apps/api/v2/test/fixtures/repository/bookings.repository.fixture.ts (1)
  • BookingsRepositoryFixture (8-44)
apps/api/v2/test/fixtures/repository/event-types.repository.fixture.ts (1)
  • EventTypesRepositoryFixture (9-62)
apps/api/v2/test/fixtures/repository/oauth-client.repository.fixture.ts (1)
  • OAuthClientRepositoryFixture (8-64)
apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts (1)
  • MembershipRepositoryFixture (6-38)
apps/api/v2/test/fixtures/repository/hosts.repository.fixture.ts (1)
  • HostsRepositoryFixture (7-27)
apps/api/v2/test/fixtures/repository/profiles.repository.fixture.ts (1)
  • ProfileRepositoryFixture (6-30)
apps/api/v2/test/fixtures/repository/managed-organizations.repository.fixture.ts (1)
  • ManagedOrganizationsRepositoryFixture (5-46)
apps/api/v2/src/app.ts (1)
  • bootstrap (27-89)
apps/api/v2/test/utils/randomString.ts (1)
  • randomString (3-6)
packages/platform/constants/api.ts (5)
  • CAL_API_VERSION_HEADER (72-72)
  • VERSION_2024_08_13 (59-59)
  • X_CAL_CLIENT_ID (50-50)
  • X_CAL_SECRET_KEY (49-49)
  • SUCCESS_STATUS (9-9)
packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts (3)
  • BookingOutput_2024_08_13 (280-306)
  • RecurringBookingOutput_2024_08_13 (308-323)
  • GetSeatedBookingOutput_2024_08_13 (325-331)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Atoms E2E Tests
🔇 Additional comments (4)
packages/trpc/server/routers/viewer/bookings/get.handler.ts (1)

102-113: Approve: admin/owner membership scoping is correct

The OR clause in packages/trpc/server/routers/viewer/bookings/get.handler.ts correctly limits admin/owner memberships to the user’s organization (teamId === orgId or team.parentId === orgId) and matches the PR objective. I also verified getBookings/getAllUserBookings are exercised by organization booking controllers and e2e tests (apps/api/v2/... and packages/trpc/server/...), so the path is covered.

Optional hardening — exclude pending/invited memberships by requiring accepted: true:

   const membershipIdsWhereUserIsAdminOwner = (
     await prisma.membership.findMany({
       where: {
         userId: user.id,
+        accepted: true,
         role: {
           in: ["ADMIN", "OWNER"],
         },
         ...(user.orgId && {
           OR: [
             {
               teamId: user.orgId,
             },
             {
               team: {
                 parentId: user.orgId,
               },
             },
           ],
         }),
       },
       select: {
         id: true,
       },
     })
   ).map((membership) => membership.id);
  • File to note: packages/trpc/server/routers/viewer/bookings/get.handler.ts (membership.findMany block above).
packages/platform/types/event-types/inputs/private-link.input.ts (1)

21-22: Doc wording aligns with intended default behavior

The description clarifies the default single-use behavior when omitted, matching prior decisions noted in PR #22304. Good consistency.

apps/api/v2/src/ee/event-types-private-links/controllers/event-types-private-links.controller.ts (1)

7-18: Import cleanup and type source-of-truth migration look good

Switching to UpdatePrivateLinkBody from platform-types and removing OmitType usage simplifies the controller and reduces duplication. No functional changes introduced; signatures remain stable.

apps/api/v2/src/modules/organizations/bookings/managed-organizations-bookings.controller.e2e-spec.ts (1)

75-112: Test bootstrap and guard overrides are appropriate

The usage of withApiAuth, guard overrides, and late app.init() is standard for this suite. Good separation of seeding helpers before app initialization.

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

📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7ef4bef and 5b71366.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • apps/api/v2/package.json (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Atoms E2E Tests

@supalarry supalarry enabled auto-merge (squash) August 14, 2025 09:11
@supalarry supalarry merged commit 378e8e0 into main Aug 14, 2025
62 of 63 checks passed
@supalarry supalarry deleted the lauris/cal-6250-fix-managed-organization-bookings branch August 14, 2025 09:30
@github-actions
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

bookings area: bookings, availability, timezones, double booking 🐛 bug Something isn't working core area: core, team members only organizations area: organizations, orgs platform Anything related to our platform plan ready-for-e2e

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants