Skip to content

Comments

feat: add team schedules#22954

Merged
Ryukemeister merged 27 commits intocalcom:mainfrom
shaun-ak:feat-team-schedules
Aug 19, 2025
Merged

feat: add team schedules#22954
Ryukemeister merged 27 commits intocalcom:mainfrom
shaun-ak:feat-team-schedules

Conversation

@shaun-ak
Copy link
Contributor

@shaun-ak shaun-ak commented Aug 7, 2025

What does this PR do?

  • Fixes feat: team schedules #22948 (GitHub issue number)
  • Fixes CAL-6213 (Linear issue number - should be visible at the bottom of the GitHub issue description)

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?

This can be tested in api v2

@shaun-ak shaun-ak requested a review from a team August 7, 2025 12:13
@shaun-ak shaun-ak requested a review from a team as a code owner August 7, 2025 12:13
@shaun-ak shaun-ak marked this pull request as draft August 7, 2025 12:13
@vercel
Copy link

vercel bot commented Aug 7, 2025

@shaun-ak 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 Aug 7, 2025
@graphite-app graphite-app bot requested a review from a team August 7, 2025 12:13
@github-actions github-actions bot added api area: API, enterprise API, access token, OAuth platform Anything related to our platform plan teams area: teams, round robin, collective, managed event-types ✨ feature New feature or request labels Aug 7, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 7, 2025

Walkthrough

Adds team schedules support: new TeamsSchedulesModule, TeamsSchedulesService, TeamsSchedulesController (GET /v2/teams/:teamId/schedules) and an organizations-team schedules endpoint (GET /v2/organizations/:orgId/teams/:teamId/schedules). Introduces SchedulesRepository_2024_06_11.getSchedulesByUserIds and updates OrganizationsSchedulesService to use that repository. Wires TeamsSchedulesModule into EndpointsModule. Updates OrganizationsModule controllers/providers (adds OrganizationsEventTypesPrivateLinksController, removes OrganizationsConferencingController). Adds e2e tests for both team and org-team schedules.

Assessment against linked issues

Objective Addressed Explanation
Add endpoint to fetch organization team schedules in organizations-teams-schedules.controller.ts, returning schedules of all users within team within org [#22948, CAL-6213]
Create teams-schedules.controller.ts with endpoint returning schedules of all users within a team (team not within organization) [#22948, CAL-6213]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Added OrganizationsEventTypesPrivateLinksController import and EventTypesPrivateLinksModule registration (apps/api/v2/src/modules/organizations/organizations.module.ts) Not related to team schedules objectives; introduces event-types private-links wiring.
Removed OrganizationsConferencingController from module controllers (apps/api/v2/src/modules/organizations/organizations.module.ts) Removal of conferencing controller is unrelated to team schedules feature.
Replaced OrganizationSchedulesRepository with SchedulesRepository_2024_06_11 in OrganizationsSchedulesService and removed OrganizationSchedulesRepository provider (apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts and apps/api/v2/src/modules/organizations/organizations.module.ts) Repository swap/refactor is not requested by the linked issues and is orthogonal to adding team schedules endpoints.

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 Docstrings
🧪 Generate unit tests
  • 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
🪧 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
Copy link

graphite-app bot commented Aug 7, 2025

Graphite Automations

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

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

"Add community label" took an action on this PR • (08/07/25)

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

"Add platform 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/19/25)

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

@Ryukemeister Ryukemeister changed the title feat: ✨add team schedules feat: add team schedules Aug 12, 2025
@alishaz-polymath alishaz-polymath added the High priority Created by Linear-GitHub Sync label Aug 12, 2025
@shaun-ak
Copy link
Contributor Author

Hey @Ryukemeister , sorry I got busy with some personal stuff and was not able to address the comments in time.
I guess the code changes are good now.

@shaun-ak shaun-ak requested a review from Ryukemeister August 14, 2025 11:08
@shaun-ak shaun-ak marked this pull request as ready for review August 14, 2025 11:08
@graphite-app graphite-app bot requested a review from a team August 14, 2025 11:08
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: 4

🧹 Nitpick comments (8)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.service.ts (2)

16-27: Parallelize schedule transformations and add early-returns + explicit return type

  • Avoids sequential awaits to reduce latency.
  • Short-circuits when there are no team users or schedules.
  • Adds an explicit return type for clarity.
-  async getOrganizationTeamSchedules(teamId: number, skip = 0, take = 250) {
+  async getOrganizationTeamSchedules(
+    teamId: number,
+    skip = 0,
+    take = 250
+  ): Promise<ScheduleOutput_2024_06_11[]> {
-    const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
-    const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);
+    const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
+    if (userIds.length === 0) return [];
+    const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);
-
-    const responseSchedules: ScheduleOutput_2024_06_11[] = [];
-
-    for (const schedule of schedules) {
-      responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
-    }
-
-    return responseSchedules;
+    if (!schedules.length) return [];
+    return Promise.all(schedules.map((s) => this.outputSchedulesService.getResponseSchedule(s)));
   }

10-14: Consider reusing existing service logic to avoid duplication

There appears to be similar logic in SchedulesService_2024_06_11.getSchedulesByUserIds(teamId,...). If feasible, delegate to that method to keep a single source of truth for team schedule assembly.

apps/api/v2/src/modules/organizations/organizations.module.ts (1)

108-109: Remove unused provider if not referenced

If the repository above is indeed unused, remove it from providers to avoid unnecessary DI registrations.

-    OrganizationsTeamsSchedulesService,
-    OrganizationsTeamsSchedulesRepository,
+    OrganizationsTeamsSchedulesService,
apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/teams-schedules.controller.ts (1)

19-35: Fix API version docs and avoid drift with version constants

  • The controller supports versions 2024-06-11 and 2024-06-14, but the header says “Must be set to 2024-06-11”.
  • Prefer using API_VERSIONS_VALUES for the controller to stay in sync with platform-wide versions.
-import { VERSION_2024_06_14, VERSION_2024_06_11 } from "@/lib/api-versions";
+import { API_VERSIONS_VALUES, VERSION_2024_06_11 } from "@/lib/api-versions";
@@
-  version: [VERSION_2024_06_14, VERSION_2024_06_11],
+  version: API_VERSIONS_VALUES,
@@
-@ApiHeader({
-  name: "cal-api-version",
-  description: `Must be set to ${VERSION_2024_06_11}`,
-  example: VERSION_2024_06_11,
-  required: true,
-  schema: {
-    default: VERSION_2024_06_11,
-  },
-})
+@ApiHeader({
+  name: "cal-api-version",
+  description: `Supported versions: ${VERSION_2024_06_11}.`,
+  example: VERSION_2024_06_11,
+  required: true,
+  schema: {
+    default: VERSION_2024_06_11,
+  },
+})
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (1)

38-41: Redundant IsTeamInOrg guard at method-level

The class already applies IsTeamInOrg. The additional method-level @UseGuards(IsTeamInOrg) is redundant.

-  @UseGuards(IsTeamInOrg)
   @Roles("TEAM_ADMIN")
   @PlatformPlan("ESSENTIALS")
   @Get("/schedules")
apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/schedules.service.ts (3)

4-5: Reduce cross-module coupling: avoid depending on OrganizationSchedulesRepository from SchedulesService

Importing OrganizationSchedulesRepository here leaks organization-layer concerns into the schedules domain. Prefer keeping schedule data access within SchedulesRepository and reusing it across org/team contexts to minimize circular dependencies and hidden module requirements.

Apply this in-file diff to drop the org repo dependency in favor of a schedules-level method:

-import { OrganizationSchedulesRepository } from "@/modules/organizations/schedules/organizations-schedules.repository";
 import { TeamsRepository } from "@/modules/teams/teams/teams.repository";
-    private readonly organizationSchedulesRepository: OrganizationSchedulesRepository

And in the method (see lines 81-92) replace the repository call:

-    const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);
+    const schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);

Add this helper to SchedulesRepository_2024_06_11 (outside this file), following our Prisma guidelines (select-only, explicit order for stable pagination):

// inside SchedulesRepository_2024_06_11
async getSchedulesByUserIds(userIds: number[], skip = 0, take = 250) {
  if (!userIds.length) return [];
  return this.prisma.schedule.findMany({
    where: { userId: { in: userIds } },
    orderBy: { id: "asc" },
    skip,
    take,
    select: {
      id: true,
      name: true,
      isDefault: true,
      timeZone: true,
      userId: true,
      // add other needed fields explicitly; never use `include`
    },
  });
}

81-81: Naming nit: method name doesn’t reflect its input

Method takes teamId, not userIds. Consider renaming to getTeamSchedules or getSchedulesByTeamUserIds for clarity.


83-84: Large teams: consider chunking IN-queries to avoid parameter limits

If teams can be large, some databases enforce limits on IN-list sizes or SQL packet size. Consider chunking uniqueUserIds into batches (e.g., 1k each) and merging results, or push filtering into a join in the repository.

📜 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 c268925 and 9217f9b.

📒 Files selected for processing (6)
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/teams-schedules.controller.ts (1 hunks)
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.module.ts (1 hunks)
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/schedules.service.ts (3 hunks)
  • apps/api/v2/src/modules/organizations/organizations.module.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.service.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:

  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/teams-schedules.controller.ts
  • apps/api/v2/src/modules/organizations/organizations.module.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/schedules.service.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.service.ts
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.module.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/ee/schedules/schedules_2024_06_11/controllers/teams-schedules.controller.ts
  • apps/api/v2/src/modules/organizations/organizations.module.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/schedules.service.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.service.ts
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.module.ts
**/*.{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/ee/schedules/schedules_2024_06_11/services/schedules.service.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.service.ts
🧬 Code Graph Analysis (3)
apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/teams-schedules.controller.ts (5)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (1)
  • Controller (24-81)
packages/platform/constants/api.ts (3)
  • VERSION_2024_06_14 (56-56)
  • VERSION_2024_06_11 (57-57)
  • SUCCESS_STATUS (9-9)
apps/api/v2/src/modules/auth/guards/api-auth/api-auth.guard.ts (1)
  • ApiAuthGuard (7-20)
apps/api/v2/src/lib/docs/headers.ts (1)
  • API_KEY_OR_ACCESS_TOKEN_HEADER (42-47)
packages/platform/types/pagination/pagination.input.ts (1)
  • SkipTakePagination (26-52)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (2)
packages/platform/types/pagination/pagination.input.ts (1)
  • SkipTakePagination (26-52)
packages/platform/constants/api.ts (1)
  • SUCCESS_STATUS (9-9)
apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.module.ts (1)
apps/api/v2/src/modules/organizations/organizations.module.ts (1)
  • Module (82-190)
🔇 Additional comments (6)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.service.ts (1)

20-24: Incorrect — OutputSchedulesService_2024_06_11 does not expose credential.key

getResponseSchedule in apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/output-schedules.service.ts returns only { id, ownerId, name, timeZone, availability, isDefault, overrides } and contains no credential or key fields. It's used by organizations-schedules.service.ts and organizations-teams-schedules.service.ts but still returns the same sanitized object. No change required.

Likely an incorrect or invalid review comment.

apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.module.ts (2)

14-22: Wiring TeamsModule into SchedulesModule looks correct

Importing TeamsModule and registering TeamsSchedulesController_2024_06_11 aligns with the new dependency on TeamsRepository.


10-10: No action needed — TeamsModule provides & exports TeamsRepository

Verified: apps/api/v2/src/modules/teams/teams/teams.module.ts lists TeamsRepository in both providers and exports (providers at line 14; exports at line 16), so the DI requirement for SchedulesService_2024_06_11 is satisfied.

  • File: apps/api/v2/src/modules/teams/teams/teams.module.ts — providers: [TeamsRepository, ...] (line 14); exports: [TeamsRepository] (line 16)
apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/teams-schedules.controller.ts (2)

56-61: Good controller delegation and response contract

The controller delegates to SchedulesService and returns a strongly-typed envelope. Looks consistent with platform patterns.


23-24: No additional guard required — RolesGuard already enforces team-scoped checks when :teamId is present

RolesGuard checks membership by teamId and will deny access if the caller isn't a team member; org/user-scoped guards (IsTeamInOrg / IsUserInOrgTeam) are available and used where appropriate.

  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/controllers/teams-schedules.controller.ts — uses @UseGuards(ApiAuthGuard, PermissionsGuard, RolesGuard, PlatformPlanGuard) and @roles("TEAM_ADMIN") on the /v2/teams/:teamId/schedules route.
  • apps/api/v2/src/modules/auth/guards/roles/roles.guard.ts — checkUserRoleAccess calls MembershipsRepository.findMembershipByTeamId(...) and throws if no membership; handles org/team combinations and system-admin bypass.
  • apps/api/v2/src/modules/auth/guards/users/is-user-in-org-team.guard.ts and apps/api/v2/src/modules/auth/guards/teams/is-team-in-org.guard.ts — exist and are applied in org-scoped/user-scoped controllers (e.g. organizations-teams-schedules.controller.ts uses IsUserInOrgTeam for user-specific endpoints).

Action: do not add @UseGuards(IsUserInTeam) here — RolesGuard already provides the required team-scoped authorization.

apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (1)

51-60: LGTM: Endpoint wiring and service usage

The endpoint properly extracts pagination, delegates to OrganizationsTeamsSchedulesService, and returns the standardized response.

Comment on lines 81 to 92
async getSchedulesByUserIds(teamId: number, skip = 0, take = 250) {
const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);

const responseSchedules = [];

for (const schedule of schedules) {
responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
}

return responseSchedules;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Harden and streamline: explicit return type, dedupe/empty-guard userIds, and parallelize mapping

  • Add the explicit return type.
  • De-duplicate user IDs and short-circuit on empty teams (avoid IN () queries that may misbehave).
  • Use Promise.all to transform results concurrently.

Apply this diff:

-  async getSchedulesByUserIds(teamId: number, skip = 0, take = 250) {
-    const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
-    const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);
-
-    const responseSchedules = [];
-
-    for (const schedule of schedules) {
-      responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
-    }
-
-    return responseSchedules;
-  }
+  async getSchedulesByUserIds(
+    teamId: number,
+    skip = 0,
+    take = 250
+  ): Promise<ScheduleOutput_2024_06_11[]> {
+    const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
+    const uniqueUserIds = Array.from(new Set(userIds));
+    if (uniqueUserIds.length === 0) return [];
+
+    const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(
+      uniqueUserIds,
+      skip,
+      take
+    );
+    return Promise.all(
+      schedules.map((s) => this.outputSchedulesService.getResponseSchedule(s))
+    );
+  }
📝 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
async getSchedulesByUserIds(teamId: number, skip = 0, take = 250) {
const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);
const responseSchedules = [];
for (const schedule of schedules) {
responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
}
return responseSchedules;
}
async getSchedulesByUserIds(
teamId: number,
skip = 0,
take = 250
): Promise<ScheduleOutput_2024_06_11[]> {
const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
const uniqueUserIds = Array.from(new Set(userIds));
if (uniqueUserIds.length === 0) return [];
const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(
uniqueUserIds,
skip,
take
);
return Promise.all(
schedules.map((s) => this.outputSchedulesService.getResponseSchedule(s))
);
}
🤖 Prompt for AI Agents
In
apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/schedules.service.ts
around lines 81 to 92, the method lacks an explicit return type, doesn't guard
against empty or duplicate team user IDs, and maps schedules serially; change
the signature to include an explicit return type (e.g.,
Promise<ResponseScheduleType[]>), after fetching userIds deduplicate them and if
the resulting array is empty return an empty array early to avoid an IN ()
query, call getSchedulesByUserIds with the deduped IDs, and use Promise.all to
concurrently map schedules to response objects via
outputSchedulesService.getResponseSchedule before returning the array.


async getSchedulesByUserIds(teamId: number, skip = 0, take = 250) {
const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
const schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Ensure stable pagination and Prisma ‘select’-only usage in repository

  • Pagination is only correct if the underlying query has a deterministic order (e.g., orderBy: { id: 'asc' }). Please confirm the repository implements ordering.
  • Per guidelines, ensure the repository uses select (never include) and only selects necessary fields.

Run this script to inspect the repository method:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locating organizations-schedules.repository.ts and getSchedulesByUserIds…"
FD_PATHS="$(fd -a -g 'organizations-schedules.repository.ts' | tr '\n' ' ')"
if [[ -z "$FD_PATHS" ]]; then
  echo "organizations-schedules.repository.ts not found"; exit 1
fi
for p in $FD_PATHS; do
  echo "---- File: $p ----"
  rg -n "getSchedulesByUserIds\\s*\\(" -A 60 "$p"
  echo
done

echo "Scanning for Prisma 'include' usage in the same file(s)…"
rg -n "include\\s*:" $FD_PATHS || echo "No 'include' found (good)"

echo "Scanning for 'orderBy' near getSchedulesByUserIds…"
for p in $FD_PATHS; do
  rg -n "getSchedulesByUserIds\\s*\\(" -n "$p" | awk -F: '{print $1":"$2}' | while IFS=: read -r file line; do
    start=$((line)); end=$((line+120)); sed -n "${start},${end}p" "$file" | rg -n "orderBy\\s*:"
  done
done

Length of output: 1957


Ensure deterministic ordering and use Prisma select (fix required)

Verified: apps/api/v2/src/modules/organizations/schedules/organizations-schedules.repository.ts — async getSchedulesByUserIds (lines ~9–21) uses prisma.schedule.findMany with include: { availability: true } and skip/take but no orderBy.

Actionable items:

  • Add a deterministic orderBy (e.g., orderBy: { id: 'asc' } or createdAt) so pagination is stable.
  • Replace Prisma include with select, selecting only the scalar/relation fields actually needed by the caller (use nested select for relations).

Suggested diff:

@@
-      include: {
-        availability: true,
-      },
-      skip,
-      take,
+      orderBy: { id: 'asc' },
+      select: {
+        id: true,
+        userId: true,
+        // other scalar fields required by the service
+        availability: {
+          select: {
+            /* only availability fields required */
+          },
+        },
+      },
+      skip,
+      take,
📝 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 schedules = await this.organizationSchedulesRepository.getSchedulesByUserIds(userIds, skip, take);
// In apps/api/v2/src/modules/organizations/schedules/organizations-schedules.repository.ts
return prisma.schedule.findMany({
// ...other existing params (where, etc.)
orderBy: { id: 'asc' },
select: {
id: true,
userId: true,
// other scalar fields required by the service
availability: {
select: {
/* only availability fields required */
},
},
},
skip,
take,
});
🤖 Prompt for AI Agents
In
apps/api/v2/src/ee/schedules/schedules_2024_06_11/services/schedules.service.ts
around line 83: the repository call getSchedulesByUserIds returns results from
prisma.findMany that currently lacks a deterministic order and uses include;
update the repository method
(apps/api/v2/src/modules/organizations/schedules/organizations-schedules.repository.ts
async getSchedulesByUserIds) to add a stable orderBy (e.g., orderBy: { id: 'asc'
} or orderBy: { createdAt: 'asc' }) so pagination is deterministic, and replace
prisma include: { availability: true } with a prisma select that only selects
the scalar fields and the specific nested availability fields the service
actually needs (use nested select for relations) to minimize data fetched and
surface only required fields to the caller.

@Ryukemeister
Copy link
Contributor

Ryukemeister commented Aug 14, 2025

Hey @Ryukemeister , sorry I got busy with some personal stuff and was not able to address the comments in time. I guess the code changes are good now.

Hey Shaunak, no worries! Since this was kind of an urgent request from an important customer I took over the PR and will finish it, and yes i've addressed the comments so no issues on that end as well, also thank you for the PR! 🙏

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

♻️ Duplicate comments (4)
apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts (1)

233-246: Use Prisma select (not include) and add deterministic ordering for pagination

Per repo guidelines, avoid include and only select needed fields. Also, add orderBy to make skip/take pagination deterministic across calls.

Apply:

-  async getSchedulesByUserIds(userIds: number[], skip: number, take: number) {
-    return this.dbRead.prisma.schedule.findMany({
-      where: {
-        userId: {
-          in: userIds,
-        },
-      },
-      include: {
-        availability: true,
-      },
-      skip,
-      take,
-    });
-  }
+  async getSchedulesByUserIds(userIds: number[], skip: number, take: number) {
+    return this.dbRead.prisma.schedule.findMany({
+      where: {
+        userId: {
+          in: userIds,
+        },
+      },
+      select: {
+        id: true,
+        userId: true,
+        name: true,
+        timeZone: true,
+        availability: {
+          select: {
+            id: true,
+            date: true,
+            days: true,
+            startTime: true,
+            endTime: true,
+          },
+        },
+      },
+      orderBy: [{ userId: "asc" }, { id: "asc" }],
+      skip,
+      take,
+    });
+  }

If OutputSchedulesService_2024_06_11.getResponseSchedule requires additional fields, adjust the select accordingly. To verify, run:

#!/bin/bash
# Inspect which schedule/availability fields are accessed in OutputSchedulesService
fd -a output-schedules.service.ts
rg -n -C3 '\bgetResponseSchedule\s*\(' $(fd output-schedules.service.ts)
rg -n -C2 '\bschedule\.' $(fd output-schedules.service.ts)
rg -n -C2 '\bavailability\.' $(fd output-schedules.service.ts)
apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts (1)

12-12: Good switch to SchedulesRepository_2024_06_11

This addresses the earlier duplication concerns by reusing a single schedules repository across modules.

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

16-27: Add explicit return type, early-return on empty team, and parallelize mapping

Also consider delegating to a single implementation to avoid duplication with getTeamSchedules.

-  async getTeamSchedulesByUserIds(teamId: number, skip = 0, take = 250) {
-    const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
-    const schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);
-
-    const responseSchedules = [];
-
-    for (const schedule of schedules) {
-      responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
-    }
-
-    return responseSchedules;
-  }
+  async getTeamSchedulesByUserIds(
+    teamId: number,
+    skip = 0,
+    take = 250
+  ): Promise<ScheduleOutput_2024_06_11[]> {
+    return this.getTeamSchedules(teamId, skip, take);
+  }

29-40: DRY the two methods and parallelize transforms

Unify logic in getTeamSchedules and let getTeamSchedulesByUserIds delegate to it. Also add an explicit return type and early-return on empty teams.

-  async getTeamSchedules(teamId: number, skip = 0, take = 250) {
-    const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
-    const schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);
-
-    const responseSchedules: ScheduleOutput_2024_06_11[] = [];
-
-    for (const schedule of schedules) {
-      responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
-    }
-
-    return responseSchedules;
-  }
+  async getTeamSchedules(
+    teamId: number,
+    skip = 0,
+    take = 250
+  ): Promise<ScheduleOutput_2024_06_11[]> {
+    const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
+    if (userIds.length === 0) return [];
+    const schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);
+    return Promise.all(schedules.map((s) => this.outputSchedulesService.getResponseSchedule(s)));
+  }
🧹 Nitpick comments (6)
apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts (2)

16-21: Early-return when org has no users

Avoids an unnecessary DB call when usersIds is empty.

-  async getOrganizationSchedules(organizationId: number, skip = 0, take = 250) {
+  async getOrganizationSchedules(organizationId: number, skip = 0, take = 250): Promise<ScheduleOutput_2024_06_11[]> {
     const users = await this.usersRepository.getOrganizationUsers(organizationId);
     const usersIds = users.map((user) => user.id);
 
+    if (usersIds.length === 0) return [];

22-29: Parallelize mapping with Promise.all and simplify return

Current loop processes transforms sequentially. Use Promise.all for throughput and add explicit return type for clarity.

-    const responseSchedules: ScheduleOutput_2024_06_11[] = [];
-
-    for (const schedule of schedules) {
-      responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
-    }
-
-    return responseSchedules;
+    return Promise.all(schedules.map((s) => this.outputSchedulesService.getResponseSchedule(s)));
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (2)

33-37: Mark injected services as readonly

Constructor-injected deps should be immutable.

-  constructor(
-    private schedulesService: SchedulesService_2024_06_11,
-
-    private teamsSchedulesService: TeamsSchedulesService
-  ) {}
+  constructor(
+    private readonly schedulesService: SchedulesService_2024_06_11,
+    private readonly teamsSchedulesService: TeamsSchedulesService
+  ) {}

39-41: Redundant IsTeamInOrg guard at method level

Already applied at class level via @UseGuards(..., IsTeamInOrg, ...). The method-level guard is redundant.

-  @UseGuards(IsTeamInOrg)
   @Roles("TEAM_ADMIN")
   @PlatformPlan("ESSENTIALS")
apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts (2)

10-14: Mark injected services as readonly

Keep DI fields immutable for clarity and safety.

-  constructor(
-    private readonly teamsRepository: TeamsRepository,
-    private readonly schedulesRepository: SchedulesRepository_2024_06_11,
-    private readonly outputSchedulesService: OutputSchedulesService_2024_06_11
-  ) {}
+  constructor(
+    private readonly teamsRepository: TeamsRepository,
+    private readonly schedulesRepository: SchedulesRepository_2024_06_11,
+    private readonly outputSchedulesService: OutputSchedulesService_2024_06_11
+  ) {}

16-40: Naming consistency: method names don’t reflect parameters

getTeamSchedulesByUserIds takes a teamId, not userIds. Consider renaming for clarity (e.g., keep only getTeamSchedules) and update controllers accordingly in a follow-up.

📜 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 sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ce8ef77 and 368f5d0.

📒 Files selected for processing (8)
  • apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/organizations.module.ts (3 hunks)
  • apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.e2e-spec.ts (4 hunks)
  • apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.e2e-spec.ts (1 hunks)
  • apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts (1 hunks)
  • apps/api/v2/src/modules/teams/schedules/teams-schedules.module.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/api/v2/src/modules/teams/schedules/teams-schedules.module.ts
  • apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.e2e-spec.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.e2e-spec.ts
  • apps/api/v2/src/modules/organizations/organizations.module.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{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/ee/schedules/schedules_2024_06_11/schedules.repository.ts
  • apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts
  • apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.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/ee/schedules/schedules_2024_06_11/schedules.repository.ts
  • apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts
  • apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.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/ee/schedules/schedules_2024_06_11/schedules.repository.ts
  • apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts
  • apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts
🧠 Learnings (1)
📚 Learning: 2025-07-28T11:50:23.946Z
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 **/*.ts : For Prisma queries, only select data you need; never use `include`, always use `select`

Applied to files:

  • apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts
🧬 Code Graph Analysis (2)
apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts (2)
apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts (1)
  • Injectable (8-30)
apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts (1)
  • Injectable (25-247)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (2)
packages/platform/types/pagination/pagination.input.ts (1)
  • SkipTakePagination (26-52)
packages/platform/constants/api.ts (1)
  • SUCCESS_STATUS (9-9)
⏰ 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). (1)
  • GitHub Check: Tests / Unit
🔇 Additional comments (2)
apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts (1)

233-246: Centralizing schedule-by-userIds in a single repository is the right call

Good consolidation; reduces duplication across org/teams flows and makes future changes easier to maintain.

apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.controller.ts (1)

42-56: Endpoint shape and pagination usage look good

Route, guards, and SkipTakePagination integration are consistent with v2 patterns.

Copy link
Contributor

@supalarry supalarry left a comment

Choose a reason for hiding this comment

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

Last comment

private readonly outputSchedulesService: OutputSchedulesService_2024_06_11
) {}

async getTeamSchedulesByUserIds(teamId: number, skip = 0, take = 250) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have getTeamSchedulesByUserIds and getTeamSchedules - their function body looks the same.

I assume we can get rid of getTeamSchedulesByUserIds (it also is named by user ids but does not take any user ids as a parameter so has an inaccurate name).

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

♻️ Duplicate comments (1)
apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts (1)

17-24: Resolved earlier feedback: RolesGuard is now correctly applied and @roles is enforced

Class-level RolesGuard combined with method-level @roles("TEAM_ADMIN") addresses the earlier gap where roles weren’t being checked.

🧹 Nitpick comments (4)
apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts (2)

24-24: Nit: Prefer @get() over @get("/")

Trailing slash is unnecessary and can be omitted for consistency with NestJS patterns.

-  @Get("/")
+  @Get()

25-31: Add Swagger success response decorator

The getTeamSchedules endpoint is missing an explicit success response schema. Since there’s no ApiSuccessResponse helper in the repo, use Nest’s built-in @ApiOkResponse to document the output DTO:

  • File:
    apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts
  • Changes:
 import {
   ApiOperation,
+  ApiOkResponse,
 } from "@nestjs/swagger";

   @ApiOperation({
     summary: "Get all team member schedules",
   })
+  @ApiOkResponse({ type: GetSchedulesOutput_2024_06_11 })
   async getTeamSchedules(
     @Param("teamId", ParseIntPipe) teamId: number,
     @Query() queryParams: SkipTakePagination
   ): Promise<GetSchedulesOutput_2024_06_11> {

This will ensure the response schema appears correctly in Swagger UI.

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

16-27: Add explicit return type, early-return on empty team, and parallelize mapping

This reduces unnecessary DB calls for empty teams and speeds up the per-schedule transformation.

-  async getTeamSchedules(teamId: number, skip = 0, take = 250) {
+  async getTeamSchedules(
+    teamId: number,
+    skip = 0,
+    take = 250
+  ): Promise<ScheduleOutput_2024_06_11[]> {
     const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
-    const schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);
-
-    const responseSchedules: ScheduleOutput_2024_06_11[] = [];
-
-    for (const schedule of schedules) {
-      responseSchedules.push(await this.outputSchedulesService.getResponseSchedule(schedule));
-    }
-
-    return responseSchedules;
+    if (!userIds.length) return [];
+    const schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);
+    return Promise.all(schedules.map((s) => this.outputSchedulesService.getResponseSchedule(s)));
   }

16-16: Nit: avoid drift between service defaults and SkipTakePagination defaults

Controller always passes validated skip/take from SkipTakePagination, so these default values are effectively unused here and can drift from the global defaults. Consider removing the defaults from the service signature or deriving them from a shared constant.

📜 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 sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 368f5d0 and a5f4c29.

📒 Files selected for processing (2)
  • apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts (1 hunks)
  • apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{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/schedules/services/teams-schedules.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/schedules/services/teams-schedules.service.ts
  • apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.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/teams/schedules/services/teams-schedules.service.ts
  • apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts
🧠 Learnings (1)
📚 Learning: 2025-07-28T11:50:23.946Z
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 **/*.ts : For Prisma queries, only select data you need; never use `include`, always use `select`

Applied to files:

  • apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts
🧬 Code Graph Analysis (2)
apps/api/v2/src/modules/teams/schedules/services/teams-schedules.service.ts (2)
apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts (1)
  • Injectable (25-247)
apps/api/v2/src/modules/organizations/schedules/services/organizations-schedules.service.ts (1)
  • Injectable (8-30)
apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts (4)
apps/api/v2/src/modules/auth/guards/api-auth/api-auth.guard.ts (1)
  • ApiAuthGuard (7-20)
apps/api/v2/src/lib/docs/headers.ts (1)
  • API_KEY_OR_ACCESS_TOKEN_HEADER (42-47)
packages/platform/types/pagination/pagination.input.ts (1)
  • SkipTakePagination (26-52)
packages/platform/constants/api.ts (1)
  • SUCCESS_STATUS (9-9)
🔇 Additional comments (1)
apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts (1)

17-19: Evaluate Plan Gating on TeamsSchedulesController

The TeamsSchedulesController (apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.controller.ts) currently only applies ApiAuthGuard and RolesGuard, whereas all other “schedules” endpoints enforce PlatformPlanGuard for paid-feature gating:

  • organizations-teams-schedules.controller.ts
  • organizations-schedules.controller.ts

If team schedules are a paid feature, add PlatformPlanGuard for consistency:

+import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard";
@@
-@UseGuards(ApiAuthGuard, RolesGuard)
+@UseGuards(ApiAuthGuard, RolesGuard, PlatformPlanGuard)

Verify whether this endpoint should be behind a paid plan and, if so, include PlatformPlanGuard accordingly.


async getTeamSchedules(teamId: number, skip = 0, take = 250) {
const userIds = await this.teamsRepository.getTeamUsersIds(teamId);
const schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Prisma: switch repository to select-only (no include) per guidelines

The called repository method currently uses include: { availability: true }. Repo guideline: only select required fields; never use include. Update SchedulesRepository_2024_06_11.getSchedulesByUserIds accordingly.

To verify current usage:

Suggested change in apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts:

   async getSchedulesByUserIds(userIds: number[], skip: number, take: number) {
     return this.dbRead.prisma.schedule.findMany({
       where: {
         userId: {
           in: userIds,
         },
       },
-      include: {
-        availability: true,
-      },
+      select: {
+        id: true,
+        userId: true,
+        name: true,
+        timeZone: true,
+        availability: {
+          select: {
+            id: true,
+            date: true,
+            startTime: true,
+            endTime: true,
+            days: true,
+          },
+        },
+      },
       skip,
       take,
     });
   }

Adjust fields to exactly what OutputSchedulesService_2024_06_11 requires.


🏁 Script executed:

#!/bin/bash
# Confirm include usage in schedules repository
rg -n 'class SchedulesRepository_2024_06_11|getSchedulesByUserIds|include\s*:' apps/api/v2/src/ee/schedules/schedules_2024_06_11 -n -C3

Length of output: 4949


Prisma: replace include with explicit select in getSchedulesByUserIds
The getSchedulesByUserIds method in
apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts
currently uses an include on the read query. Per our Prisma guideline—only select the fields you need; never use include on read operations—please refactor this to use a select block instead:

--- a/apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts
+++ b/apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts
@@ async getSchedulesByUserIds(userIds: number[], skip: number, take: number) {
-      include: {
-        availability: true,
-      },
+      select: {
+        id: true,
+        userId: true,
+        name: true,
+        timeZone: true,
+        availability: {
+          select: {
+            id: true,
+            date: true,
+            startTime: true,
+            endTime: true,
+            days: true,
+          },
+        },
+      },

Adjust the selected fields to exactly match what OutputSchedulesService_2024_06_11 consumes.

📝 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 schedules = await this.schedulesRepository.getSchedulesByUserIds(userIds, skip, take);
// apps/api/v2/src/ee/schedules/schedules_2024_06_11/schedules.repository.ts
async getSchedulesByUserIds(userIds: number[], skip: number, take: number) {
return this.prisma.schedule.findMany({
where: { userId: { in: userIds } },
skip,
take,
- include: {
- availability: true,
select: {
id: true,
userId: true,
name: true,
timeZone: true,
availability: {
select: {
id: true,
date: true,
startTime: true,
endTime: true,
days: true,
},
},
},
});
}

@supalarry supalarry self-requested a review August 19, 2025 10:11
supalarry
supalarry previously approved these changes Aug 19, 2025
Copy link
Contributor

@supalarry supalarry left a comment

Choose a reason for hiding this comment

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

Good stuff!

@Ryukemeister Ryukemeister enabled auto-merge (squash) August 19, 2025 10:11
ThyMinimalDev
ThyMinimalDev previously approved these changes Aug 19, 2025
Copy link
Contributor

@ThyMinimalDev ThyMinimalDev left a comment

Choose a reason for hiding this comment

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

LGTM

@github-actions
Copy link
Contributor

github-actions bot commented Aug 19, 2025

E2E results are ready!

@Ryukemeister Ryukemeister disabled auto-merge August 19, 2025 10:39
@Ryukemeister Ryukemeister enabled auto-merge (squash) August 19, 2025 10:39
Copy link
Member

@alishaz-polymath alishaz-polymath left a comment

Choose a reason for hiding this comment

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

👏

@vercel
Copy link

vercel bot commented Aug 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Aug 19, 2025 11:59am
cal-eu Ignored Ignored Aug 19, 2025 11:59am

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

♻️ Duplicate comments (3)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.e2e-spec.ts (3)

237-239: Good: asserting exact count (2) addresses earlier feedback

Switching from “> 0” to an exact length of 2 tightens the contract and aligns with the test setup (admin + member).


240-245: Good: replaced brittle filter()[0] with find() and existence checks

Using find() and asserting definedness prevents undefined access and produces clearer failures. Thanks for addressing the earlier comment.


250-253: Fix incorrect variable in the last assertion (timeZone)

The final assertion compares userOneSchedule?.timeZone to user2Schedule.timeZone. It should use userTwoSchedule to validate the second user’s schedule. With both schedules sharing the same time zone, this bug wouldn’t fail, so please correct it.

Apply this diff:

-          expect(userOneSchedule?.timeZone).toEqual(user2Schedule.timeZone);
+          expect(userTwoSchedule?.timeZone).toEqual(user2Schedule.timeZone);
🧹 Nitpick comments (1)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.e2e-spec.ts (1)

79-88: Strengthen assertions by using different time zones for each user

Both schedules use the same time zone ("America/New_York"), which can mask variable-mixup bugs in assertions. Use a different time zone for user2 to make the test robust against mistakes (see fix suggested below for the final assertion).

Example (outside this hunk): set user2’s schedule to a different time zone:

user2Schedule = await scheduleRepositoryFixture.create({
  user: { connect: { id: user2.id } },
  name: `organizations-teams-schedules-user2-schedule-${randomString()}`,
  timeZone: "Europe/London",
});
📜 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 sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a5f4c29 and 717a224.

📒 Files selected for processing (2)
  • apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.e2e-spec.ts (4 hunks)
  • apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.e2e-spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/v2/src/modules/teams/schedules/controllers/teams-schedules.e2e-spec.ts
🧰 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/teams/schedules/organizations-teams-schedules.e2e-spec.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/teams/schedules/organizations-teams-schedules.e2e-spec.ts
🧬 Code Graph Analysis (1)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.e2e-spec.ts (2)
apps/api/v2/test/utils/randomString.ts (1)
  • randomString (3-6)
packages/platform/constants/api.ts (1)
  • SUCCESS_STATUS (9-9)
⏰ 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). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (2)
apps/api/v2/src/modules/organizations/teams/schedules/organizations-teams-schedules.e2e-spec.ts (2)

21-21: Type import aligns with aggregator response shape

Bringing in GetSchedulesOutput_2024_06_11 for the team-aggregate response is correct and improves type safety in the test.


41-42: Explicit schedule fixture typing is clear

Declaring userSchedule and user2Schedule as Schedule keeps the fixtures self-documenting and prevents accidental misuse.

@Ryukemeister Ryukemeister merged commit 167cf6e into calcom:main Aug 19, 2025
34 of 39 checks passed
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 community Created by Linear-GitHub Sync ✨ feature New feature or request High priority Created by Linear-GitHub Sync 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.

feat: team schedules

5 participants