Skip to content

Conversation

@visz11
Copy link

@visz11 visz11 commented Dec 12, 2025

User description

What does this PR do?

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

Visual Demo (For contributors especially)

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

Video Demo (if applicable):

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

Image Demo (if applicable):

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

Mandatory Tasks (DO NOT REMOVE)

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

How should this be tested?

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

Checklist

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

CodeAnt-AI Description

Add configurable multi-host assignment for Round Robin bookings

What Changed

  • Event types gain a new optional setting maxRoundRobinHosts to control how many hosts can be automatically assigned to a single Round Robin booking (defaults to 1 / existing behavior).
  • Booking flow now selects up to maxRoundRobinHosts available hosts per booking (assigns all available hosts when fewer than configured) and validates availability for recurring slots; selection loop and host lookups were optimized for performance.
  • API, database schema, form state, UI form control, OpenAPI docs, default event data, and unit/e2e tests were added/updated to expose and verify this behavior.

Impact

✅ Assign up to N hosts per round-robin booking
✅ Fewer failed bookings when multiple hosts are available
✅ Clearer API + UI control for host count

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added ability to configure the maximum number of hosts assigned per booking for Round Robin event types (defaults to 1)
  • Documentation

    • Updated API and integration documentation for new maxRoundRobinHosts parameter on event types

✏️ Tip: You can customize this high-level summary in your review settings.

Add maxRoundRobinHosts config to assign up to N hosts per RR booking.
Includes schema, API v2, booking logic, UI, and tRPC integration.
Default (null/1) maintains existing single-host behavior

feat: add multi-host assignment for round robin bookings

Add maxRoundRobinHosts config to assign up to N hosts per RR booking.
Includes schema, API v2, booking logic, UI, and tRPC integration.
Default (null/1) maintains existing single-host behavior
- Add 4 unit tests covering multi-host assignment scenarios
- Update event-types-hooks.mdx with maxRoundRobinHosts documentation
- Improve API field description in DTOs for better OpenAPI docs
- Test create round robin event type with maxRoundRobinHosts
- Test default value (null) when not provided
- Test update maxRoundRobinHosts value
- Use Set for faster user lookups
- Move userIdsSet outside the loop
- Move async calls (firstUserOrgId, enrichedHosts) outside loops
- Filter group hosts once per group instead of every iteration
@codeant-ai
Copy link

codeant-ai bot commented Dec 12, 2025

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@visz11
Copy link
Author

visz11 commented Dec 12, 2025

@refacto-visz

@coderabbitai
Copy link

coderabbitai bot commented Dec 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This pull request introduces support for maxRoundRobinHosts, a configurable feature that limits the maximum number of hosts assigned per booking in Round Robin event types. The change spans the database schema, API types and services, repository queries, form components, booking logic, and comprehensive test coverage with documentation updates.

Changes

Cohort / File(s) Summary
Database Schema & Migration
packages/prisma/schema.prisma, packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql
Added nullable maxRoundRobinHosts integer column to EventType table and updated Prisma schema.
API Platform Types
packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts, packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts, packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts
Added optional maxRoundRobinHosts field to CreateTeamEventTypeInput, UpdateTeamEventTypeInput, and TeamEventTypeOutput with validation (Int, Min=1) and API documentation.
API Services & Output
apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts, apps/api/v2/src/modules/organizations/event-types/services/output.service.ts
Expanded Input type unions and output shapes to include and propagate maxRoundRobinHosts field through response objects.
Repository Query Selection
packages/features/eventtypes/repositories/eventTypeRepository.ts, packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
Added maxRoundRobinHosts: true to Prisma select schemas in findById, findByIdForOrgAdmin, findForSlots, and getEventTypesFromDBSelect.
Form Types & Defaults
packages/features/eventtypes/lib/types.ts, packages/features/eventtypes/lib/defaultEvents.ts, packages/lib/test/builder.ts
Added maxRoundRobinHosts: number | null to FormValues and EventTypeSetup types; included in commons/dynamicEvent defaults and buildEventType test helper.
Form Components & Hooks
packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx, packages/platform/atoms/event-types/hooks/useEventTypeForm.ts
Added NumberInput form field for maxRoundRobinHosts in RoundRobinHosts component with validation (min=1); updated useEventTypeForm to initialize field from eventType.
Booking Service Logic
packages/features/bookings/lib/service/RegularBookingService.ts
Refactored round-robin host selection to support per-group max hosts, introducing maxHostsPerGroup tracking, enriched host pre-filtering, excluded user tracking, and group-based host assignment loop with updated control flow and state management.
E2E & Unit Tests
apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts, packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts
Added comprehensive E2E test suite for maxRoundRobinHosts API functionality and unit tests validating host assignment behavior with multiple max host scenarios.
Documentation & Localization
docs/api-reference/v2/openapi.json, docs/platform/event-types-hooks.mdx, apps/web/public/static/locales/en/common.json
Updated OpenAPI schema with deprecation messages, expanded useCreateTeamEventType hook documentation with maxRoundRobinHosts parameter, and added translation strings for UI labels and descriptions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • RegularBookingService.ts warrants careful attention: the refactored host-selection logic introduces new control-flow with per-group host tracking, excluded user sets, and group-based filtering; verify correctness of the maxHostsPerGroup bounds and excludedUserIds state management across iterations.
  • Round-robin test suite: Review the test scenarios covering maxRoundRobinHosts limits (>1, undefined, =1) to ensure expectations align with the booking service implementation.
  • Repository & service propagation: Most other changes are straightforward field additions and Prisma select extensions; verify consistent inclusion across all three repository methods and output services.

Poem

🐰 A tale of hosts and bookings fair,
Where round-robin spreads the care,
Now maxRoundRobinHosts takes the stage,
Capping hosts per booking page—
One host or more, the choice runs deep! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Feat/multi host round robin' directly describes the main feature being implemented across the entire changeset: adding maxRoundRobinHosts functionality to enable multiple hosts per Round Robin booking.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/multi-host-round-robin

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

❤️ Share

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

@gemini-code-assist
Copy link

Summary of Changes

Hello @visz11, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to Round Robin event types by allowing the configuration of a maximum number of hosts to be assigned per booking. Previously, Round Robin events would typically assign a single host. With this change, users can now specify maxRoundRobinHosts, enabling a more flexible and collaborative scheduling experience where multiple team members can be automatically assigned to an event. This feature is supported across the API, database, and user interface, ensuring a complete and well-tested implementation.

Highlights

  • Multi-Host Round Robin Scheduling: Introduced a new maxRoundRobinHosts property for Round Robin event types, allowing multiple hosts to be assigned to a single booking.
  • API and Database Integration: The maxRoundRobinHosts property has been integrated into the API input/output types, database schema, and event type repositories.
  • User Interface Enhancement: A new input field for maxRoundRobinHosts has been added to the event type assignment tab in the UI, allowing users to configure this setting.
  • Comprehensive Testing: New end-to-end tests and unit tests have been added to validate the correct behavior of maxRoundRobinHosts in various booking scenarios, including default values and multiple host assignments.
  • Documentation Updates: API reference and platform documentation have been updated to reflect the new maxRoundRobinHosts parameter and its usage, ensuring clarity for developers and users.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codeant-ai codeant-ai bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Dec 12, 2025
@visz11
Copy link
Author

visz11 commented Dec 12, 2025

@refacto-visz

Copy link

@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: 5

🧹 Nitpick comments (10)
docs/platform/event-types-hooks.mdx (1)

141-148: Align wording with the public API: hook “options object” vs mutation “payload object”.
The narrative says the “mutation function accepts an object…”, but your PR summary indicates the hook now accepts an options object (and the mutation receives the payload). Consider explicitly separating:

  • useCreateTeamEventType(hookOptions)
  • createTeamEventType(payload) (including maxRoundRobinHosts)

Also applies to: 155-164

docs/api-reference/v2/openapi.json (2)

13844-13844: Tighten the 200-response description formatting for readability

Line 13844 has a long, indented, multi-sentence string; consider reflowing to avoid embedded extra whitespace (the \n ) so rendered docs don’t show odd indentation.


19663-19671: Use integer + explicit OpenAPI constraints for maxRoundRobinHosts

maxRoundRobinHosts is described as min 1, default 1, but is declared as "type": "number" (Lines 19668, 21686, 22154), which permits floats and doesn’t encode the default/min. Prefer:

  • "type": "integer" (optionally "format": "int32")
  • "minimum": 1
  • "default": 1

Suggested diff (apply in all three locations):

   "maxRoundRobinHosts": {
-    "type": "number",
+    "type": "integer",
+    "format": "int32",
+    "minimum": 1,
+    "default": 1,
     "description": "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
     "example": 1
   }

Also applies to: 21681-21689, 22149-22157

packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (2)

517-541: LGTM! New maxRoundRobinHosts input field.

The implementation correctly handles the nullable integer field with appropriate min constraint and value parsing. The null fallback for empty string ensures the field can be cleared.

One minor observation: consider adding a max attribute or validation to prevent unreasonably large values (e.g., max={100} or matching the team size).


281-282: Unused parameter orgId.

The orgId parameter is aliased to _orgId but never used within RoundRobinHosts. If this is intentional for API compatibility, the underscore prefix is appropriate. Otherwise, consider removing it from the props interface.

packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql (1)

1-2: Migration looks correct.

The nullable INTEGER column aligns with the application logic that defaults to 1 when null.

Consider adding a CHECK constraint to enforce positive values at the database level:

ALTER TABLE "public"."EventType" ADD COLUMN "maxRoundRobinHosts" INTEGER CHECK ("maxRoundRobinHosts" IS NULL OR "maxRoundRobinHosts" >= 1);

This would prevent invalid data from bypassing application validation.

packages/prisma/schema.prisma (1)

249-249: Consider adding a database-level default value.

The field is defined as Int? without a @default() directive. This means:

  • New EventType records will have maxRoundRobinHosts = null
  • The application layer must handle the null case

If the intended behavior is to default to 1 (as suggested by the test builder and API docs), consider adding:

-  maxRoundRobinHosts Int?
+  maxRoundRobinHosts Int? @default(1)

This would provide consistency and reduce the need for null-checking throughout the application. If null is intentional to represent "not configured" or "no limit", the current schema is appropriate.

packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts (1)

555-563: Consider conditional validation + a sane upper bound for maxRoundRobinHosts.
Right now the DTO allows this value regardless of schedulingType, and it also allows arbitrarily large ints. If the field is intended to apply only to round-robin, consider enforcing that here (or explicitly rejecting it for COLLECTIVE) and bounding the max to prevent pathological inputs.

@@
-import {
+import {
   IsString,
   IsInt,
   IsBoolean,
   IsOptional,
   Min,
+  ValidateIf,
+  Max,
   ValidateNested,
   IsArray,
   ArrayNotEmpty,
   ArrayUnique,
   IsUrl,
   IsIn,
 } from "class-validator";
@@
   @IsInt()
   @Min(1)
   @IsOptional()
+  @ValidateIf((o: UpdateTeamEventTypeInput_2024_06_14) => o.schedulingType === SchedulingType.ROUND_ROBIN)
+  @Max(50)
   @DocsPropertyOptional({
     description: "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
     example: 1,
+    default: 1,
   })
   maxRoundRobinHosts?: number;
apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts (2)

77-100: Consider removing the @ts-ignore by fixing the type definition.

The @ts-ignore on line 84 indicates a type mismatch between the API's expected input (string "roundRobin") and the CreateTeamEventTypeInput_2024_06_14 type definition. The input type should accept the string literal union if that's what the API expects.

  const createRoundRobinEventType = async (
    overrides: Partial<CreateTeamEventTypeInput_2024_06_14> = {}
  ): Promise<TeamEventTypeOutput_2024_06_14> => {
    const body: CreateTeamEventTypeInput_2024_06_14 = {
      title: "Round Robin Event",
      slug: `max-rr-hosts-${randomString()}`,
      lengthInMinutes: 30,
-     // @ts-ignore - schedulingType accepts string
-     schedulingType: "roundRobin",
+     schedulingType: "roundRobin" as const,
      assignAllTeamMembers: true,
      ...overrides,
    };

Alternatively, verify that CreateTeamEventTypeInput_2024_06_14.schedulingType accepts the string literal "roundRobin" in the type definition.


199-205: Consider sequential cleanup to improve error visibility.

Using Promise.all for cleanup could mask individual deletion failures. For test reliability, consider sequential cleanup or wrapping in try-catch.

  afterAll(async () => {
-   await Promise.all(managedUsers.map((u) => userRepositoryFixture.delete(u.user.id)));
+   for (const u of managedUsers) {
+     await userRepositoryFixture.delete(u.user.id);
+   }
    await userRepositoryFixture.delete(platformAdmin.id);
    await oauthClientRepositoryFixture.delete(oAuthClient.id);
    await teamRepositoryFixture.delete(organization.id);
    await app.close();
  });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f350542 and 11315b8.

📒 Files selected for processing (20)
  • apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/event-types/services/output.service.ts (3 hunks)
  • apps/web/public/static/locales/en/common.json (1 hunks)
  • docs/api-reference/v2/openapi.json (7 hunks)
  • docs/platform/event-types-hooks.mdx (2 hunks)
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts (1 hunks)
  • packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts (1 hunks)
  • packages/features/bookings/lib/service/RegularBookingService.ts (11 hunks)
  • packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (7 hunks)
  • packages/features/eventtypes/lib/defaultEvents.ts (1 hunks)
  • packages/features/eventtypes/lib/types.ts (1 hunks)
  • packages/features/eventtypes/repositories/eventTypeRepository.ts (3 hunks)
  • packages/lib/test/builder.ts (1 hunks)
  • packages/platform/atoms/event-types/hooks/useEventTypeForm.ts (1 hunks)
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts (1 hunks)
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts (1 hunks)
  • packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts (1 hunks)
  • packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql (1 hunks)
  • packages/prisma/schema.prisma (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (2)
packages/features/eventtypes/lib/types.ts (1)
  • FormValues (84-182)
packages/ui/components/form/inputs/Input.tsx (1)
  • NumberInput (170-182)
🔇 Additional comments (20)
docs/platform/event-types-hooks.mdx (1)

133-134: Verify/qualify “defaults to 1” (and where that default is enforced).
If the default is enforced server-side, call that out explicitly; if it’s only a UI/form default, the docs should avoid implying an API default.

docs/api-reference/v2/openapi.json (1)

29460-29463: Adding message to only 2 output envelopes: confirm consistency/contract

Adding a top-level message field is generally backward compatible, but please confirm whether this “informational/deprecation” field should be part of the standard response envelope across endpoints (for consistency), or intentionally limited to these two outputs (and why).

Also applies to: 29491-29494

packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (1)

104-110: Good defensive guard for optional callback.

The null check before invoking onChange prevents potential runtime errors when the callback is not provided.

packages/features/bookings/lib/service/RegularBookingService.ts (3)

1047-1070: Multi-host round robin selection logic looks correct.

The implementation properly:

  • Defaults maxHostsPerGroup to 1 when maxRoundRobinHosts is null
  • Pre-computes enrichedHosts and userIdsSet outside the loop for efficiency
  • Filters groupRRHosts to the current group context

One consideration: if eventType.maxRoundRobinHosts is explicitly set to 0, the while loop condition hostsFoundInGroup < maxHostsPerGroup (i.e., 0 < 0) will never be true, effectively disabling host selection. Verify this is the intended behavior or add validation upstream.


1072-1136: Loop termination and state management are correct.

The while loop properly handles:

  • Finding multiple hosts per group up to maxHostsPerGroup
  • Excluding already-selected users via excludedUserIds
  • Correctly differentiating between successful selection (increment counter) and availability failure (exclude but don't increment)

The break on line 1088 when newLuckyUser is null ensures the loop terminates if no suitable host is found.


1156-1161: Availability guard handles multi-host scenario correctly.

The condition luckyUsers.length < (Object.keys(nonEmptyHostGroups).length || 1) ensures at least one host is selected per non-empty group. Since maxRoundRobinHosts is a maximum (not a required minimum), finding fewer hosts than the max is acceptable when the pool is exhausted.

packages/platform/atoms/event-types/hooks/useEventTypeForm.ts (2)

122-122: Form default value correctly initialized.

The maxRoundRobinHosts field is properly added to the form's default values. Since the ...input spread in handleSubmit (line 378) passes through fields not explicitly destructured, maxRoundRobinHosts will be included in the payload when dirty.


302-331: Field correctly flows through submit payload.

The maxRoundRobinHosts field is not explicitly destructured from dirtyValues, so it correctly remains in the input object, then flows through rest to the final payload. This ensures it's submitted when the field is dirty.

packages/features/eventtypes/lib/types.ts (1)

171-171: LGTM!

The type definition for maxRoundRobinHosts is consistent with the Prisma schema and follows the established pattern for optional numeric fields in FormValues.

apps/web/public/static/locales/en/common.json (1)

4108-4109: Translation keys look good; verify key usage matches UI exactly.
Since these are new keys, the main risk is a mismatch between the UI lookup key and the JSON key (typos / different naming convention).

packages/features/eventtypes/lib/defaultEvents.ts (1)

128-130: maxRoundRobinHosts: null is fine, but confirm downstream treats null/undefined as “use default (1)”.
Because this value is now present on default event shapes, anything that reads it should handle null safely (especially if it expects a number).

packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts (1)

18-185: Good: Prisma select now hydrates maxRoundRobinHosts.
This ensures the field actually makes it into the returned event type object via ...restEventType.

packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts (1)

641-649: LGTM! Well-documented optional field for round robin host limits.

The validation decorators (@IsInt(), @Min(1), @IsOptional()) are appropriate, and the documentation clearly explains the field's purpose and behavior when fewer hosts are available than the configured maximum.

packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts (2)

1089-1170: LGTM! Well-structured test helpers and scenario setup.

The helper functions effectively reduce duplication and the setupScenario function correctly handles the optional maxRoundRobinHosts parameter with a conditional spread.


1191-1237: Comprehensive test coverage for maxRoundRobinHosts scenarios.

The four test cases cover the essential scenarios:

  • Multiple hosts assignment when maxRoundRobinHosts > 1
  • Graceful degradation when fewer hosts are available than configured
  • Default behavior (single host) when undefined
  • Explicit single host when set to 1
apps/api/v2/src/modules/organizations/event-types/services/output.service.ts (1)

107-108: LGTM! Consistent propagation of maxRoundRobinHosts through the output service.

The field is correctly destructured and included in the response object, following the same pattern as rescheduleWithSameRoundRobinHost.

Also applies to: 143-143

packages/features/eventtypes/repositories/eventTypeRepository.ts (3)

606-606: LGTM! Correctly added to CompleteEventTypeSelect in findById.

The field is properly positioned alongside related round robin configuration fields.


905-905: LGTM! Consistently added to findByIdForOrgAdmin.

Maintains parity with findById for organization admin access patterns.


1265-1265: Critical addition for slot calculation logic.

Including maxRoundRobinHosts in findForSlots ensures the booking service has access to this configuration when determining host assignment during slot availability calculations.

apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts (1)

207-230: Good test coverage for maxRoundRobinHosts CRUD operations.

The tests effectively validate:

  • Setting maxRoundRobinHosts during creation
  • Default null value when not provided (backward compatibility)
  • Updating the value on an existing event type

Comment on lines +97 to 98
| "maxRoundRobinHosts"
>;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the correct file and verify it exists
find . -name "output-event-types.service.ts" -type f

Repository: coderabbit-test/cal.com

Length of output: 161


🏁 Script executed:

#!/bin/bash
# List the entire file to understand its structure
cat -n apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts | head -250

Repository: coderabbit-test/cal.com

Length of output: 9831


🏁 Script executed:

#!/bin/bash
# Search for maxRoundRobinHosts in the file
rg -n "maxRoundRobinHosts" apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts -B 3 -A 3

Repository: coderabbit-test/cal.com

Length of output: 247


maxRoundRobinHosts is accepted in the Input type but not exposed in the API response.

The field is added to the Input type (line 97) but neither extracted in the destructuring block (lines 107-138) nor included in the return object (lines 173-218). Unlike maxActiveBookingsPerBooker and maxActiveBookingPerBookerOfferReschedule, which have a corresponding transformation method, maxRoundRobinHosts has no processing logic. Either add it to the response output or remove it from the Input type if intentionally unsupported.

🤖 Prompt for AI Agents
In
apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts
around lines 97 to 218, the Input type includes maxRoundRobinHosts but it is
neither destructured from input (lines ~107-138) nor included in the returned
API response (lines ~173-218) and there is no transformation method; update the
service to handle this field by (1) adding maxRoundRobinHosts to the
destructuring where other Input fields are extracted, (2) applying the same
transformation/validation pattern used for maxActiveBookingsPerBooker (create a
small transform method if necessary) or pass through the value consistently, and
(3) include the transformed/maxRoundRobinHosts value in the returned object so
the API exposes it; alternatively, if this field is intentionally unsupported,
remove it from the Input type to keep types and output consistent.

Comment on lines +14455 to 14456
"summary": "Get team event types",
"description": "Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts \"asc\" (oldest first) or \"desc\" (newest first). When not provided, no explicit ordering is applied.",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

“sortCreatedAt” description mentions “by ID” — confirm that’s accurate

Line 14456 says ordering is “by creation date (by ID)”; if the actual sort is createdAt, drop the “(by ID)” phrasing (or rename the param / clarify the implementation) to avoid misleading consumers.

🤖 Prompt for AI Agents
In docs/api-reference/v2/openapi.json around lines 14455 to 14456, the
summary/description for the sortCreatedAt query parameter incorrectly says "by
creation date (by ID)"; verify the actual implementation and then update the
description to match: if the API sorts by the createdAt timestamp, remove the
parenthetical "(by ID)" and state "order results by creation date (createdAt)";
if the API actually sorts by ID instead of createdAt, either change the
parameter name to reflect ID-based sorting or update the description to say
"order results by ID" to avoid misleading consumers.

Comment on lines +133 to 134
The useCreateTeamEventType hook allows you to create a new team event type. This hook returns a mutation function that handles the event type creation process. The mutation function accepts an object with the following properties: ***lengthInMinutes*** which is the length of the event in minutes, ***title*** which is the title of the event, ***slug*** which is the slug of the event, ***description*** which is the description of the event, schedulingType which can be either ***COLLECTIVE***, ***ROUND_ROBIN*** or ***MANAGED***, ***hosts*** which is an array of hosts for the event and the ***teamId*** which is the id of the team, and optionally ***maxRoundRobinHosts*** which specifies the maximum number of hosts to assign per booking for Round Robin events (defaults to 1).
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify when maxRoundRobinHosts applies + what happens for non-ROUND_ROBIN scheduling types.
Right now it reads Round Robin-specific, but the example below uses schedulingType: "COLLECTIVE". Document whether the API ignores it, rejects it, or only accepts it for ROUND_ROBIN (and ideally show a ROUND_ROBIN example if that’s the intended use).

🤖 Prompt for AI Agents
In docs/platform/event-types-hooks.mdx around lines 133 to 134, the docstring
for useCreateTeamEventType is ambiguous about maxRoundRobinHosts; update the
text to state explicitly that maxRoundRobinHosts only applies when
schedulingType is "ROUND_ROBIN", explain whether the API ignores or rejects it
for other scheduling types (e.g., "ignored" or "validated" per implementation),
and replace or add an example showing schedulingType: "ROUND_ROBIN" with
maxRoundRobinHosts set (or note that the field is optional and defaults to 1) so
readers know intended usage and behavior for non-ROUND_ROBIN types.

durationLimits: null,
assignAllTeamMembers: false,
rescheduleWithSameRoundRobinHost: false,
maxRoundRobinHosts: 1,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -nP --type=ts -C5 'maxRoundRobinHosts' --glob '!**/test/**' --glob '!**/builder.ts'

Repository: coderabbit-test/cal.com

Length of output: 23937


🏁 Script executed:

# Check test builder to understand the context of the hardcoded value
head -150 packages/lib/test/builder.ts | tail -20

Repository: coderabbit-test/cal.com

Length of output: 687


Use null instead of 1 for maxRoundRobinHosts in test builder to match form defaults.

The test builder hardcodes maxRoundRobinHosts: 1, but the form defaults use null (see packages/features/eventtypes/lib/defaultEvents.ts:129). While the booking logic correctly handles this via nullish coalescing (?? 1 in RegularBookingService.ts:1047), the test builder should match the actual form defaults for consistency.

🤖 Prompt for AI Agents
In packages/lib/test/builder.ts at line 143, the test builder currently sets
maxRoundRobinHosts: 1 which differs from the form default of null; change that
property to maxRoundRobinHosts: null so the test data matches form defaults (and
update any tests that assert on the numeric value to expect null or use the
service's nullish defaulting).

Comment on lines +554 to +561
@IsInt()
@Min(1)
@IsOptional()
@ApiPropertyOptional({
description: "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
example: 1,
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify default value behavior in API documentation.

The description states "defaults to 1", but the field is marked @IsOptional() and the schema allows null. This could confuse API consumers about the actual behavior:

  • If null/undefined means "use default of 1", the description is correct but the API should document that null1
  • If null means "no limit" or "not configured", the description should be updated to clarify this

Please update the description to accurately reflect when/where the default value of 1 is applied (e.g., "When not specified, defaults to 1" vs "Defaults to 1 when null").

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new feature allowing users to specify maxRoundRobinHosts for Round Robin event types, controlling the maximum number of hosts assigned per booking. The changes include adding the maxRoundRobinHosts field to the database schema, API input/output types, and internal data structures. The core logic in RegularBookingService.ts was updated to respect this new limit during host assignment, ensuring that bookings assign up to the specified number of hosts per group, or all available hosts if fewer than the maximum. A new E2E test suite was added to verify the functionality of maxRoundRobinHosts during event type creation and updates, covering scenarios where the value is set, undefined, or equal to one. The UI was updated with a new number input field for this setting, and documentation (API reference and platform hooks) was updated to reflect the new property. A review comment highlighted that the existing tests for maxRoundRobinHosts only cover single-group scenarios and suggested adding tests for multiple round-robin groups to ensure the limit is applied correctly as a total, not per-group. Another comment requested correcting a misleading example in the documentation where maxRoundRobinHosts was shown with a COLLECTIVE scheduling type instead of ROUND_ROBIN.

} from "@calcom/platform-types";
import type { PlatformOAuthClient, Team, User } from "@calcom/prisma/client";

describe("maxRoundRobinHosts for Round Robin event types", () => {

Choose a reason for hiding this comment

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

medium

This is a great test suite for the maxRoundRobinHosts feature. However, it currently only covers scenarios with a single team/group.

The implementation in RegularBookingService.ts has a potential issue with how it handles multiple round-robin groups, where it might assign more hosts than the maxRoundRobinHosts limit.

To ensure robust testing, I recommend adding test cases that involve multiple round-robin groups to verify that maxRoundRobinHosts is correctly applied as a total limit for the booking, not as a per-group limit.

title: "Daily standup",
slug: "daily-standup",
description: "Daily standup for the team",
schedulingType: "COLLECTIVE",

Choose a reason for hiding this comment

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

medium

The example for useCreateTeamEventType is a bit misleading. It shows schedulingType: "COLLECTIVE" while also setting maxRoundRobinHosts. However, maxRoundRobinHosts is only applicable for ROUND_ROBIN event types, as stated in the description.

To avoid confusion, please update the example to use schedulingType: "ROUND_ROBIN".

            schedulingType: "ROUND_ROBIN",

@visz11
Copy link
Author

visz11 commented Dec 12, 2025

@refacto-visz


const response = await request(app.getHttpServer())
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
.set(X_CAL_SECRET_KEY, oAuthClient.secret)
Copy link

Choose a reason for hiding this comment

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

Suggestion: The managed user creation request only sets the secret header and omits the client ID header, which is required by other OAuth-client-authenticated endpoints in this codebase; this can cause the call to /api/v2/oauth-clients/:id/users to be rejected as unauthorized even though a valid client is configured. [possible bug]

Severity Level: Critical 🚨

Suggested change
.set(X_CAL_SECRET_KEY, oAuthClient.secret)
.set(X_CAL_CLIENT_ID, oAuthClient.id)
Why it matters? ⭐

This test file uses the OAuth client id + secret as request headers elsewhere (see other requests in the same file where both X_CAL_CLIENT_ID and X_CAL_SECRET_KEY are set). The managed-user creation call only sets the secret header which could plausibly be rejected by auth middleware that expects both headers. Adding the missing X_CAL_CLIENT_ID header is a small, correct change that fixes a real authorization mismatch rather than a stylistic nit.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts
**Line:** 60:60
**Comment:**
	*Possible Bug: The managed user creation request only sets the secret header and omits the client ID header, which is required by other OAuth-client-authenticated endpoints in this codebase; this can cause the call to `/api/v2/oauth-clients/:id/users` to be rejected as unauthorized even though a valid client is configured.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

const { setValue, getValues, control } = useFormContext<FormValues>();
const assignRRMembersUsingSegment = getValues("assignRRMembersUsingSegment");
const isRRWeightsEnabled = useWatch({
control,
Copy link

Choose a reason for hiding this comment

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

Suggestion: The value for assignRRMembersUsingSegment is read once via getValues and never subscribed, so if that form field is toggled elsewhere the RoundRobinHosts component (and EditWeightsForAllTeamMembers) will keep using a stale value, which is a logic bug; you should use useWatch for this field just like the others so the UI reacts to changes correctly. [logic error]

Severity Level: Minor ⚠️

Suggested change
control,
const assignRRMembersUsingSegment = useWatch({
control,
name: "assignRRMembersUsingSegment",
});
Why it matters? ⭐

The current code uses getValues to read assignRRMembersUsingSegment once, so it won't react to updates made elsewhere in the form. Replacing this with useWatch (like the other fields) fixes a real logic bug: the component and any dependent child components (e.g. EditWeightsForAllTeamMembers) will correctly re-render when that field changes. This is not a cosmetic change — it fixes stale-state behavior.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
**Line:** 307:307
**Comment:**
	*Logic Error: The value for `assignRRMembersUsingSegment` is read once via `getValues` and never subscribed, so if that form field is toggled elsewhere the `RoundRobinHosts` component (and `EditWeightsForAllTeamMembers`) will keep using a stale value, which is a logic bug; you should use `useWatch` for this field just like the others so the UI reacts to changes correctly.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

schedulingType: "COLLECTIVE",
hosts: [{"userId": 1456}, {"userId": 2357}],
teamId: 1234,
maxRoundRobinHosts: 2,
Copy link

Choose a reason for hiding this comment

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

Suggestion: The example sets maxRoundRobinHosts on an event configured with a COLLECTIVE scheduling type even though the text explains this option only affects Round Robin events, which can mislead developers into expecting this field to change behaviour for collective events when it will be ignored; adding an inline clarification avoids this incorrect assumption while keeping the example runnable. [possible bug]

Severity Level: Critical 🚨

Suggested change
maxRoundRobinHosts: 2,
maxRoundRobinHosts: 2, // Only used when schedulingType is "ROUND_ROBIN"
Why it matters? ⭐

The PR documentation text explicitly states maxRoundRobinHosts only applies to Round Robin events, but the example object sets that field while using schedulingType: "COLLECTIVE". Adding a short inline comment prevents readers from mistakenly thinking the field has an effect for COLLECTIVE events. This is a small, clear clarification in a doc example (no runtime change) and helps prevent confusion when copying the snippet.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** docs/platform/event-types-hooks.mdx
**Line:** 163:163
**Comment:**
	*Possible Bug: The example sets `maxRoundRobinHosts` on an event configured with a `COLLECTIVE` scheduling type even though the text explains this option only affects Round Robin events, which can mislead developers into expecting this field to change behaviour for collective events when it will be ignored; adding an inline clarification avoids this incorrect assumption while keeping the example runnable.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

durationLimits: null,
assignAllTeamMembers: false,
rescheduleWithSameRoundRobinHost: false,
maxRoundRobinHosts: 1,
Copy link

Choose a reason for hiding this comment

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

Suggestion: The test builder sets maxRoundRobinHosts to 1 by default, while the Prisma schema declares this field as optional (Int?) and the shared default event configuration uses null; this mismatch means tests using buildEventType() will create event types with an explicit max-hosts limit even when the feature is conceptually "not configured", diverging from real runtime defaults and potentially hiding bugs around the distinction between null and an explicit 1. [logic error]

Severity Level: Minor ⚠️

Suggested change
maxRoundRobinHosts: 1,
maxRoundRobinHosts: null,
Why it matters? ⭐

The suggestion highlights a real mismatch between the test fixture and the production model: an optional Prisma Int? field semantically differs from an explicit value of 1. Using 1 in the test builder will mask behavior where the field is unset (null) in real data and can hide bugs that depend on distinguishing "not configured" vs "explicitly limited to 1". Changing the fixture to null aligns tests with runtime defaults and improves fidelity.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/lib/test/builder.ts
**Line:** 143:143
**Comment:**
	*Logic Error: The test builder sets `maxRoundRobinHosts` to `1` by default, while the Prisma schema declares this field as optional (`Int?`) and the shared default event configuration uses `null`; this mismatch means tests using `buildEventType()` will create event types with an explicit max-hosts limit even when the feature is conceptually "not configured", diverging from real runtime defaults and potentially hiding bugs around the distinction between `null` and an explicit `1`.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +560 to +561
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

Suggestion: The new output field is typed and documented as a non-nullable number, but the database and services can return null for this value; this mismatch (number vs number|null and missing nullable: true in the Swagger decorator) breaks the API contract and can cause consumers to treat a possibly-null value as always numeric, leading to runtime errors when they perform numeric operations. [type error]

Severity Level: Minor ⚠️

Suggested change
})
maxRoundRobinHosts?: number;
nullable: true,
})
maxRoundRobinHosts?: number | null;
Why it matters? ⭐

The suggestion fixes a real typing/contract inconsistency. Other optional numeric fields in this file that can be null (see parentEventTypeId and ownerId patterns in the same output) include nullable: true in the ApiPropertyOptional and use a union type (number | null). The current code marks maxRoundRobinHosts as optional but not nullable in the schema and as a plain number type, which could mislead consumers and swagger clients into treating it as always numeric. Making it nullable in the decorator and typing it number | null aligns runtime behavior, the OpenAPI contract, and the project's existing patterns.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts
**Line:** 560:561
**Comment:**
	*Type Error: The new output field is typed and documented as a non-nullable number, but the database and services can return null for this value; this mismatch (number vs number|null and missing `nullable: true` in the Swagger decorator) breaks the API contract and can cause consumers to treat a possibly-null value as always numeric, leading to runtime errors when they perform numeric operations.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@refacto-visz
Copy link

refacto-visz bot commented Dec 12, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

@codeant-ai
Copy link

codeant-ai bot commented Dec 12, 2025

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Migration & client generation
    Adding maxRoundRobinHosts changes the Prisma schema — this requires creating and applying a proper migration and regenerating Prisma/Kysely clients. Failing to run migrations or regenerate generated code will cause runtime mismatches and type errors across the codebase.

  • Prisma schema sync
    A SQL migration was added directly. Verify the Prisma schema has been updated to include this field (optional vs required) and that generated client code is rebuilt so runtime types match the DB change.

  • Possible Bug
    The PR adds maxRoundRobinHosts to multiple Prisma select objects. If the Prisma schema doesn't have this field (or migrations weren't run), these queries will break at compile/runtime. Verify the database schema and the generated Prisma client include this field and that all affected selects are type-correct.

  • Missing default / backfill
    The new column is added without a DEFAULT or any migration to populate existing rows. If application code expects a non-null default (e.g., 0) this will cause runtime surprises. Consider whether existing rows need a backfill and whether a DEFAULT or NOT NULL constraint is required.

  • Unmapped Field
    The newly added maxRoundRobinHosts property is present in the Input type but is not used anywhere in the code path that builds the output. Confirm whether this should be propagated to the response object (and how it should be named/typed in the output). If intended to be part of the API output, it must be read from databaseEventType, validated/transformed if necessary, and included in the returned EventTypeOutput object.

@refacto-visz
Copy link

refacto-visz bot commented Dec 12, 2025

Code Review: Multi-Host Round Robin Booking Feature

PR Confidence Score: 🟥 3 / 5

👍 Well Done
Comprehensive Feature Implementation

Feature is consistently implemented across the database, API, UI, and core logic, demonstrating a solid vertical slice with excellent test coverage.

Optimized Host Data Fetching

Refactored booking logic to fetch host delegation credentials once, avoiding repeated async calls within a loop.

📁 Selected files for review (20)
  • apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts
  • apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts
  • packages/features/eventtypes/lib/defaultEvents.ts
  • packages/features/eventtypes/lib/types.ts
  • packages/features/eventtypes/repositories/eventTypeRepository.ts
  • packages/lib/test/builder.ts
  • packages/platform/atoms/event-types/hooks/useEventTypeForm.ts
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts
  • packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts
  • packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql
  • packages/prisma/schema.prisma
  • apps/api/v2/src/modules/organizations/event-types/services/output.service.ts
  • apps/web/public/static/locales/en/common.json
  • docs/api-reference/v2/openapi.json
  • docs/platform/event-types-hooks.mdx
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
  • packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts
  • packages/features/bookings/lib/service/RegularBookingService.ts
  • packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
📝 Additional Comments
packages/features/bookings/lib/service/RegularBookingService.ts (2)
Sequential User Selection Loop

The logic for selecting multiple hosts iterates up to maxRoundRobinHosts times, making a sequential await call to luckyUserService.getLuckyUser in each iteration. This pattern causes booking creation time to increase linearly with the number of hosts selected, potentially creating a bottleneck if getLuckyUser involves I/O. Consider refactoring to a batch operation that selects multiple lucky users in a single call to improve efficiency.

Standards:

  • ISO-IEC-25010-Performance-Efficiency-Time-Behavior
  • Optimization-Pattern-Batch-Processing
  • Algorithmic-Complexity-Linear-Optimization
Extract Host Selection Logic

The handler function's complexity is increased by adding multi-host selection logic for round-robin event types. This logic, which iterates through host groups to find multiple available hosts, could be extracted into a dedicated service or function. This refactoring would improve adherence to the Single Responsibility Principle, reduce the size of the handler, and make the host selection mechanism easier to test and maintain independently.

Standards:

  • SOLID-SRP
  • Clean-Code-Functions
packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (1)
Client Validation Bypass

Client-side validation only enforces minimum value without maximum boundary check. While server-side validation is primary security control, consistent client-side validation improves user experience and provides defense in depth. Attackers can bypass client validation but inconsistent validation patterns may indicate server-side gaps.

Standards:

  • CWE-20
  • OWASP-A04
  • NIST-SSDF-PW.1

Comment on lines 1062 to +1063
for (const [groupId, luckyUserPool] of Object.entries(luckyUserPools)) {
let luckUserFound = false;
while (luckyUserPool.length > 0 && !luckUserFound) {
const freeUsers = luckyUserPool.filter(
(user) => !luckyUsers.concat(notAvailableLuckyUsers).find((existing) => existing.id === user.id)
);
let hostsFoundInGroup = 0;
Copy link

Choose a reason for hiding this comment

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

Incorrect Multi-Host Selection Logic

The logic for selecting multiple hosts for a round-robin booking is flawed when host groups are configured. The counter hostsFoundInGroup is re-initialized for each host group, causing the system to select up to maxRoundRobinHosts from each group, rather than a total for the entire booking. This results in assigning num_groups * maxRoundRobinHosts hosts, which contradicts the feature's intent and the user-facing description of "Maximum hosts per booking".

        let totalHostsFound = 0;
        for (const [groupId, luckyUserPool] of Object.entries(luckyUserPools)) {
          let hostsFoundInGroup = 0;
          const remainingHostsNeeded = maxHostsPerGroup - totalHostsFound;
          
          const groupRRHosts = enrichedHosts.filter(
            (host) =>
              !host.isFixed &&
              userIdsSet.has(host.user.id) &&
              (host.groupId === groupId || (!host.groupId && groupId === DEFAULT_GROUP_ID))
          );
          
          while (luckyUserPool.length > 0 && hostsFoundInGroup < remainingHostsNeeded && totalHostsFound < maxHostsPerGroup) {
Commitable Suggestion
Suggested change
for (const [groupId, luckyUserPool] of Object.entries(luckyUserPools)) {
let luckUserFound = false;
while (luckyUserPool.length > 0 && !luckUserFound) {
const freeUsers = luckyUserPool.filter(
(user) => !luckyUsers.concat(notAvailableLuckyUsers).find((existing) => existing.id === user.id)
);
let hostsFoundInGroup = 0;
let totalHostsFound = 0;
for (const [groupId, luckyUserPool] of Object.entries(luckyUserPools)) {
let hostsFoundInGroup = 0;
const remainingHostsNeeded = maxHostsPerGroup - totalHostsFound;
const groupRRHosts = enrichedHosts.filter(
(host) =>
!host.isFixed &&
userIdsSet.has(host.user.id) &&
(host.groupId === groupId || (!host.groupId && groupId === DEFAULT_GROUP_ID))
);
while (luckyUserPool.length > 0 && hostsFoundInGroup < remainingHostsNeeded && totalHostsFound < maxHostsPerGroup) {
Standards
  • Algorithm-Correctness-Loop-Logic
  • Business-Rule-State-Consistency

Comment on lines +642 to +649
@IsInt()
@Min(1)
@IsOptional()
@DocsPropertyOptional({
description: "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
example: 1,
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

Missing Maximum Validation

Input validation only enforces minimum value but lacks maximum boundary validation. Attackers could submit extremely large values causing resource exhaustion through excessive host assignment operations. System performance degradation possible through unbounded resource allocation in booking processing.

Standards
  • CWE-770
  • OWASP-A04
  • NIST-SSDF-PW.1

Comment on lines +556 to +563
@IsInt()
@Min(1)
@IsOptional()
@DocsPropertyOptional({
description: "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
example: 1,
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

Missing Maximum Validation

Update endpoint validation mirrors create endpoint vulnerability with missing maximum boundary validation. Attackers could modify existing event types with excessive host counts causing resource exhaustion. System performance impact through unbounded host assignment operations in booking workflows.

Standards
  • CWE-770
  • OWASP-A04
  • NIST-SSDF-PW.1

Comment on lines +554 to +561
@IsInt()
@Min(1)
@IsOptional()
@ApiPropertyOptional({
description: "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
example: 1,
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

Missing Maximum Validation

Output validation lacks maximum boundary constraints allowing dangerous values to persist in API responses. While output validation is less critical than input, consistent validation prevents data integrity issues and ensures safe client-side processing. Resource exhaustion possible if clients process unbounded values.

Standards
  • CWE-770
  • OWASP-A04
  • NIST-SSDF-PW.1

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

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants