Skip to content

Comments

fix: Add new route to create team routing-form response again(after revert earlier)#22407

Merged
hariombalhara merged 4 commits intomainfrom
add-teams-routing-forms-response-record-endpoint-back
Jul 17, 2025
Merged

fix: Add new route to create team routing-form response again(after revert earlier)#22407
hariombalhara merged 4 commits intomainfrom
add-teams-routing-forms-response-record-endpoint-back

Conversation

@hariombalhara
Copy link
Member

@hariombalhara hariombalhara commented Jul 11, 2025

It is the same changes as in #22347. They were earlier reverted because we didn't want to introduce a breaking change in the endpoint.

Thanks to @supalarry I realized that there is no breaking change here and it is adding back the same changes with just more tests.

@github-actions
Copy link
Contributor

github-actions bot commented Jul 11, 2025

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

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

Details:

No release type found in pull request title "Revert "revert: "fix: Add new route to create team routing-form response (#22347)" (#22399)"". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

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

@keithwillcode keithwillcode added core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO labels Jul 11, 2025
Copy link
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@hariombalhara hariombalhara force-pushed the add-teams-routing-forms-response-record-endpoint-back branch from 2a2521a to 6c8f6ea Compare July 11, 2025 05:34
@vercel
Copy link

vercel bot commented Jul 11, 2025

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

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
cal ⬜️ Ignored (Inspect) Visit Preview Jul 16, 2025 4:07pm
cal-eu ⬜️ Ignored (Inspect) Visit Preview Jul 16, 2025 4:07pm

@delve-auditor
Copy link

delve-auditor bot commented Jul 11, 2025

No security or compliance issues detected. Reviewed everything up to 84e2690.

Security Overview
  • 🔎 Scanned files: 15 changed file(s)
Detected Code Changes

The diff is too large to display a summary of code changes.

Reply to this PR with @delve-auditor followed by a description of what change you want and we'll auto-submit a change to this PR to implement it.

@hariombalhara hariombalhara changed the title Revert "revert: "fix: Add new route to create team routing-form response (#22347)" (#22399)" fix: Add new route to create team routing-form response again(after revert earlier) Jul 11, 2025
@hariombalhara hariombalhara force-pushed the add-teams-routing-forms-response-record-endpoint-back branch from 6c8f6ea to 5aaae1e Compare July 11, 2025 05:47
@@ -0,0 +1,104 @@
import { ExecutionContext, CanActivate } from "@nestjs/common";
Copy link
Member Author

Choose a reason for hiding this comment

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

No change in this file, when adding back

@@ -0,0 +1,51 @@
import { Injectable, CanActivate, ExecutionContext, Type, Logger } from "@nestjs/common";
Copy link
Member Author

Choose a reason for hiding this comment

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

No change in this file, when adding back

@@ -0,0 +1,39 @@
import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.strategy";
Copy link
Member Author

Choose a reason for hiding this comment

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

No change in this file, when adding back

@hariombalhara hariombalhara force-pushed the add-teams-routing-forms-response-record-endpoint-back branch from 5aaae1e to 9f1d0ed Compare July 11, 2025 05:54
@@ -0,0 +1,810 @@
import { bootstrap } from "@/app";
Copy link
Member Author

@hariombalhara hariombalhara Jul 11, 2025

Choose a reason for hiding this comment

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

These are the same tests as intrdouced earlier in the PR, with the difference of passing version in the request now.

const createUnauthenticatedRequest = (method: 'get' | 'post' | 'patch', url: string) => {
return request(app.getHttpServer())
[method](url)
.set({ "cal-api-version": VERSION_2025_07_11 });
Copy link
Member Author

Choose a reason for hiding this comment

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

Passing version now.

path: "/v2/organizations/:orgId/routing-forms/:routingFormId/responses",
version: API_VERSIONS_VALUES,
// Default version when no version header is provided
version: VERSION_2024_04_15,
Copy link
Member Author

Choose a reason for hiding this comment

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

Now there is no change in this controller except this version change.

): Promise<CreateRoutingFormResponseOutput> {
const result = await this.organizationsRoutingFormsResponsesService.createRoutingFormResponseWithSlots(
orgId,
routingFormId,
Copy link
Member Author

Choose a reason for hiding this comment

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

orgId variable was unused

@@ -0,0 +1,121 @@
import { VERSION_2025_07_11_VALUE } from "@/lib/api-versions";
Copy link
Member Author

Choose a reason for hiding this comment

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

This is same as the Controller that was reviewed earlier in the PR #22347 (files), except the versioning difference


@Controller({
path: "/v2/organizations/:orgId/routing-forms/:routingFormId/responses",
version: VERSION_2025_07_11_VALUE,
Copy link
Member Author

Choose a reason for hiding this comment

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

Now this controller runs for this version only.

@@ -9,13 +9,27 @@ import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
import { IsRoutingFormInTeam } from "@/modules/auth/guards/routing-forms/is-routing-form-in-team.guard";
Copy link
Member Author

Choose a reason for hiding this comment

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

Same as reviewed earlier in the PR

/**
* Even when no version header is provided, we default to this version.
*/
export const DEFAULT_API_VERSION: VersionValue = VERSION_2024_04_15 as unknown as VersionValue;
Copy link
Member Author

Choose a reason for hiding this comment

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

defaultVersion: VERSION_2024_04_15,

Default Version is this, just giving it a logical name here.

@Controller({
path: "/v2/organizations/:orgId/routing-forms/:routingFormId/responses",
version: API_VERSIONS_VALUES,
version: DEFAULT_API_VERSION,
Copy link
Member Author

Choose a reason for hiding this comment

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

Using this controller only when API version is default

@@ -1,3 +1,5 @@
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
Copy link
Member Author

Choose a reason for hiding this comment

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

Same file as earlier reviewed

@@ -0,0 +1,225 @@
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
Copy link
Member Author

Choose a reason for hiding this comment

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

Same file as earlier reviewed

@@ -1,13 +1,18 @@
import { CreateRoutingFormResponseInput } from "@/modules/organizations/routing-forms/inputs/create-routing-form-response.input";
Copy link
Member Author

Choose a reason for hiding this comment

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

Same file as earlier reviewed

Comment on lines 1 to 3
import { OrganizationsRoutingFormsRepository } from "@/modules/organizations/routing-forms/organizations-routing-forms.repository";
import { SharedRoutingFormResponseService } from "@/modules/organizations/routing-forms/services/shared-routing-form-response.service";
import { OrganizationsTeamsRoutingFormsResponsesOutputService } from "@/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses-output.service";
Copy link
Member Author

Choose a reason for hiding this comment

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

Same file as revikewed earlier

Comment on lines +1 to 3
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
Copy link
Member Author

Choose a reason for hiding this comment

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

Same file as reviewed earlier

@hariombalhara hariombalhara marked this pull request as ready for review July 11, 2025 10:12
@hariombalhara hariombalhara force-pushed the add-teams-routing-forms-response-record-endpoint-back branch from b462556 to 33fc158 Compare July 11, 2025 10:12
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

cubic found 5 issues across 17 files. Review them in cubic.dev

React with 👍 or 👎 to teach cubic. Tag @cubic-dev-ai to give specific feedback.

@hariombalhara hariombalhara force-pushed the add-teams-routing-forms-response-record-endpoint-back branch from 33fc158 to 5442b2a Compare July 11, 2025 10:24
@github-actions
Copy link
Contributor

github-actions bot commented Jul 11, 2025

E2E results are ready!

joeauyeung
joeauyeung previously approved these changes Jul 15, 2025
Copy link
Contributor

@joeauyeung joeauyeung left a comment

Choose a reason for hiding this comment

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

Reviewed previous PR and ok with the changes made. Waiting for @calcom/platform for approval.

@keithwillcode keithwillcode added the High priority Created by Linear-GitHub Sync label Jul 16, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 16, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between 1c169f9 and c27e244.

📒 Files selected for processing (14)
  • apps/api/v2/src/modules/auth/guards/or-guard/index.ts (1 hunks)
  • apps/api/v2/src/modules/auth/guards/or-guard/or.guard.spec.ts (1 hunks)
  • apps/api/v2/src/modules/auth/guards/or-guard/or.guard.ts (1 hunks)
  • apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts (25 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (6 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts (3 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts (8 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts (3 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service.ts (2 hunks)
  • apps/api/v2/swagger/documentation.json (2 hunks)
 __________________________________________________
< Deploying the charm offensive against your bugs. >
 --------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).

Walkthrough

This change set introduces a new API version (2025-07-11) and enhancements to routing form response handling in the organizations and teams modules. It adds a composite guard decorator Or and a new guard IsUserRoutingForm for user-specific authorization, with comprehensive tests for the Or guard. The creation of routing form responses with slot availability is refactored into a new shared service SharedRoutingFormResponseService, which is integrated into both organization- and team-scoped services. A new POST endpoint for creating routing form responses at the team level is added, along with corresponding OpenAPI schema updates. The routing form responses controller applies the new guards selectively to support flexible authorization. End-to-end tests are extended for permission checks, validation, routing behavior, and API version restrictions. Module configurations are updated to register new services, repositories, and guards.

Tip

CodeRabbit can use oxc to improve the quality of JavaScript and TypeScript code reviews.

Add a configuration file to your project to customize how CodeRabbit runs oxc.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link
Contributor

@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 (2)
apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts (1)

445-445: Remove console.log statement

-          console.log("responseBody", responseBody.data);
apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts (1)

150-153: Fix potential crash when destination is a relative path

The new URL(destination) will throw if destination is a relative path. Consider using a base URL or handling relative paths appropriately.

     return {
-      redirectUrl: new URL(destination),
+      redirectUrl: destination.startsWith('http') ? new URL(destination) : new URL(destination, request.get('origin') || 'https://cal.com'),
       customMessage: null,
     };
🧹 Nitpick comments (6)
apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts (2)

20-29: Consider type safety for user.id conversion.

The conversion Number(user.id) assumes user.id is a valid numeric string. Consider adding validation or using a more robust parsing approach.

-        userId: Number(user.id),
+        userId: parseInt(user.id, 10),

Additionally, you might want to validate that the conversion is successful:

+    const userId = parseInt(user.id, 10);
+    if (isNaN(userId)) {
+      throw new ForbiddenException("Invalid user ID format");
+    }
+
     const userRoutingForm = await this.dbRead.prisma.app_RoutingForms_Form.findFirst({
       where: {
         id: routingFormId,
-        userId: Number(user.id),
+        userId: userId,
         teamId: null,
       },

31-35: Consider security implications of exposing user ID in error messages.

The error message includes the user ID, which could be considered sensitive information. Consider using a more generic error message in production.

-      throw new ForbiddenException(
-        `Routing Form with id=${routingFormId} is not a user Routing Form owned by user with id=${user.id}.`
-      );
+      throw new ForbiddenException(
+        `Access denied: Routing Form with id=${routingFormId} is not accessible by the current user.`
+      );
apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts (1)

187-187: Simplify boolean assignment

-      skipContactOwner: urlParams.get("cal.skipContactOwner") === "true" ? true : false,
+      skipContactOwner: urlParams.get("cal.skipContactOwner") === "true",
apps/api/v2/swagger/documentation.json (3)

5137-5143: duration example is a string but the schema is numeric
The example "60" conflicts with the "type": "number" declaration and will trigger validation warnings in many tooling chains.

-            "example": "60",
+            "example": 60,

23565-23569: Prefer integer over number for database IDs (responseId)
The same rationale as for path parameters: IDs are integral, not floating point.

-            "type": "number",
+            "type": "integer",

23610-23613: eventTypeId should also be integer
Keeping all ID fields consistent simplifies client typing and reduces precision-loss risk.

-            "type": "number",
+            "type": "integer",
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c9a47dd and 5442b2a.

📒 Files selected for processing (16)
  • apps/api/v2/src/lib/api-versions.ts (1 hunks)
  • apps/api/v2/src/modules/auth/guards/or-guard/index.ts (1 hunks)
  • apps/api/v2/src/modules/auth/guards/or-guard/or.guard.spec.ts (1 hunks)
  • apps/api/v2/src/modules/auth/guards/or-guard/or.guard.ts (1 hunks)
  • apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts (25 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (6 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts (3 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts (1 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts (8 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts (2 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts (3 hunks)
  • apps/api/v2/src/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service.ts (2 hunks)
  • apps/api/v2/swagger/documentation.json (7 hunks)
  • packages/platform/constants/api.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (11)
apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/src/lib/api-versions.ts (1)
Learnt from: alishaz-polymath
PR: calcom/cal.com#22304
File: packages/features/eventtypes/components/MultiplePrivateLinksController.tsx:92-94
Timestamp: 2025-07-16T06:42:27.001Z
Learning: In the MultiplePrivateLinksController component (packages/features/eventtypes/components/MultiplePrivateLinksController.tsx), the `currentLink.maxUsageCount ?? 1` fallback in the openSettingsDialog function is intentional. Missing maxUsageCount values indicate old/legacy private links that existed before the expiration feature was added, and they should default to single-use behavior (1) for backward compatibility.
apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/swagger/documentation.json (1)

undefined

<retrieved_learning>
Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
</retrieved_learning>

apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts (2)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.497Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.
🧬 Code Graph Analysis (3)
apps/api/v2/src/modules/auth/guards/or-guard/or.guard.spec.ts (1)
apps/api/v2/src/modules/auth/guards/or-guard/or.guard.ts (1)
  • Or (9-49)
apps/api/v2/src/lib/api-versions.ts (1)
packages/platform/constants/api.ts (7)
  • API_VERSIONS (63-70)
  • VERSION_2024_06_14 (56-56)
  • VERSION_2024_06_11 (57-57)
  • VERSION_2024_04_15 (58-58)
  • VERSION_2024_08_13 (59-59)
  • VERSION_2024_09_04 (60-60)
  • VERSION_2025_07_11 (61-61)
apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts (1)
apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts (1)
  • ApiAuthGuardUser (37-37)
🪛 Biome (1.9.4)
apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts

[error] 187-187: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

🔇 Additional comments (31)
apps/api/v2/src/modules/auth/guards/or-guard/index.ts (1)

1-1: LGTM: Clean index export pattern

The export statement follows standard module organization conventions for NestJS guards.

apps/api/v2/src/modules/organizations/teams/routing-forms/organizations-teams-routing-forms.module.ts (3)

1-1: Module import properly structured

The EventTypesRepository_2024_06_14 import follows the established pattern for versioned repositories.


6-6: Shared service integration looks good

The SharedRoutingFormResponseService import enables the centralized response creation logic as intended.


36-36: Provider registrations are correct

Both SharedRoutingFormResponseService and EventTypesRepository_2024_06_14 are properly registered as providers to enable dependency injection.

Also applies to: 45-45

packages/platform/constants/api.ts (2)

61-61: API version constant correctly defined

The VERSION_2025_07_11 constant follows the established naming convention and date format.


69-69: Array addition maintains proper structure

The new version is correctly appended to the API_VERSIONS array, maintaining the existing structure.

apps/api/v2/src/modules/organizations/routing-forms/organizations-routing-forms.module.ts (3)

1-2: Dependencies properly imported

Both EventTypesRepository_2024_06_14 and IsUserRoutingForm imports are correctly structured and necessary for the new functionality.


19-19: Shared service import enables centralized logic

The SharedRoutingFormResponseService import supports the refactoring to centralize routing form response creation logic.


24-24: Provider registrations correctly configured

All three new providers (IsUserRoutingForm, SharedRoutingFormResponseService, EventTypesRepository_2024_06_14) are properly registered for dependency injection.

Also applies to: 30-30, 33-33

apps/api/v2/src/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses.service.ts (3)

1-3: Import statements properly structured

The imports for CreateRoutingFormResponseInput, CreateRoutingFormResponseOutputData, SharedRoutingFormResponseService, and Request are correctly added to support the new functionality.

Also applies to: 6-6


15-15: Constructor injection correctly implemented

The SharedRoutingFormResponseService is properly injected into the constructor, enabling the delegation pattern.


44-54: Clean delegation pattern implemented

The createRoutingFormResponseWithSlots method properly delegates to the shared service, maintaining a clean separation of concerns and avoiding code duplication.

apps/api/v2/src/modules/auth/guards/organizations/is-user-routing-form.guard.ts (1)

14-18: Verify routingFormId parameter extraction and add validation.

The guard properly extracts the routingFormId from request parameters and validates its presence. The error message is descriptive and helps with debugging.

apps/api/v2/src/lib/api-versions.ts (3)

10-10: Good addition of new API version constant.

The new version constant is properly imported and will be used for the new versioned routing form endpoints.


13-13: Spread operator usage maintains proper type handling.

The use of spread operator for API_VERSIONS_VALUES is correct and ensures the array remains mutable as required by the VersionValue type.


20-23: Excellent addition of DEFAULT_API_VERSION constant.

Adding a named constant for the default API version improves code readability and maintainability. The documentation comment clearly explains its purpose.

apps/api/v2/src/modules/auth/guards/or-guard/or.guard.ts (3)

9-49: Excellent implementation of OR guard factory.

The OR guard implementation is well-designed with proper:

  • Factory pattern for creating composite guards
  • Dependency injection using ModuleRef
  • Error handling that allows the OR chain to continue
  • Logging for debugging and monitoring
  • Async/await pattern for guard evaluation

The logic correctly implements OR semantics: access is granted if any guard passes, and exceptions from individual guards don't halt the evaluation chain.


27-37: Good error handling strategy for OR logic.

The error handling correctly catches exceptions from individual guards and continues evaluating the remaining guards. This is the expected behavior for OR logic where one guard's failure shouldn't prevent others from being checked.


41-44: Proper final error handling.

The guard appropriately re-throws the last error if all guards fail, providing meaningful feedback while maintaining the OR semantics.

apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.ts (1)

83-105: Well-implemented POST endpoint for routing form responses.

The new endpoint follows excellent patterns:

  • Proper role-based access control (@Roles("TEAM_MEMBER"))
  • Appropriate platform plan restriction (@PlatformPlan("ESSENTIALS"))
  • Consistent parameter validation and typing
  • Standard response format with SUCCESS_STATUS
  • Proper delegation to service layer

The implementation is consistent with existing endpoints in the controller and follows NestJS best practices.

apps/api/v2/src/modules/auth/guards/or-guard/or.guard.spec.ts (3)

5-31: Well-designed mock guards for testing.

The mock guard implementations provide good test coverage by simulating different behaviors:

  • MockGuard1 and MockGuard2 for pass/fail scenarios
  • MockGuard3 for error throwing scenarios

The mock designs are simple and effective for testing the OR guard logic.


52-94: Comprehensive test coverage for OR guard behavior.

The test suite excellently covers all critical scenarios:

  • First guard passing grants access
  • Second guard passing grants access (OR logic)
  • All guards failing denies access
  • Error handling continues checking other guards

The tests properly mock dependencies and validate the expected OR semantics.


97-104: Good validation of factory function.

The test properly validates that the Or factory function returns a constructor function, ensuring the factory pattern works correctly.

apps/api/v2/src/modules/organizations/routing-forms/services/organizations-routing-forms-responses.service.ts (1)

44-54: Clean refactoring to delegate shared logic

The extraction of routing form response creation logic to SharedRoutingFormResponseService improves code reusability and maintainability.

apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts (2)

27-60: Well-structured test data organization

The grouping of test data into orgAdminData, teamData, and nonOrgAdminData improves test readability and maintainability.


701-848: Comprehensive test coverage for new API version

The tests thoroughly validate the new authorization behavior, ensuring non-org-admin users can only access their own routing forms while org admins retain full access.

apps/api/v2/src/modules/organizations/teams/routing-forms/controllers/organizations-teams-routing-forms-responses.controller.e2e-spec.ts (1)

255-702: Excellent test coverage for new team routing form response endpoint

The test suite comprehensively covers permission checks, validation, routing behavior, and error scenarios for the new POST endpoint.

apps/api/v2/src/modules/organizations/routing-forms/services/shared-routing-form-response.service.ts (1)

26-108: Well-structured service with clear separation of concerns

The service effectively centralizes routing form response creation logic with proper error handling and CRM parameter extraction.

apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (2)

39-41: Good approach to maintain backward compatibility

Using UNVERSIONED to exclude the new version ensures existing clients continue to work with the original endpoint behavior.


105-127: Well-implemented versioned endpoint with flexible authorization

The use of Or guard to allow either org admin or user routing form access provides the needed flexibility while maintaining security.

apps/api/v2/swagger/documentation.json (1)

2547-2547: Confirm the new operationId matches controller decorator
If the NestJS @ApiOperation({ operationId: … }) (or auto-generated name) in OrganizationsRoutingFormsResponsesController wasn’t updated to the exact string createRoutingFormResponseV2025_07_11, Swagger ↔ code linkage will break and the route won’t be mapped in generated SDKs.
Please double-check the controller signature.

supalarry
supalarry previously approved these changes Jul 16, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/api/v2/swagger/documentation.json (1)

5093-5106: Use integer not number for path identifiers

orgId and teamId represent discrete IDs; OpenAPI distinguishes integer from number (float).
We’ve flagged this in an earlier review and the issue persists here.

-            "schema": {
-              "type": "number"
-            }
+            "schema": {
+              "type": "integer",
+              "format": "int64"
+            }
🧹 Nitpick comments (1)
apps/api/v2/swagger/documentation.json (1)

5151-5154: Clarify numeric duration format

duration is declared simply as number; most client SDKs map this to double.
If the value must be a whole number of minutes, specify it explicitly:

-              "type": "number"
+              "type": "integer",
+              "format": "int32",
+              "minimum": 1
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5442b2a and e293fc1.

📒 Files selected for processing (2)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (6 hunks)
  • apps/api/v2/swagger/documentation.json (8 hunks)
🧰 Additional context used
🧠 Learnings (2)
apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (1)
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
apps/api/v2/swagger/documentation.json (1)

undefined

<retrieved_learning>
Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
</retrieved_learning>

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Security Check
🔇 Additional comments (4)
apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (4)

2-2: Well-structured imports and backward compatibility setup.

The imports are correctly organized and the UNVERSIONED constant provides a clean way to maintain backward compatibility while introducing the new API version. This approach ensures existing clients continue to work without modifications.

Also applies to: 8-8, 11-11, 26-26, 39-40


46-46: Good approach to selective authorization.

Moving the RolesGuard from class-level to individual methods provides flexibility for implementing different authorization strategies across endpoints while maintaining existing behavior for backward compatibility.

Also applies to: 57-57, 85-85, 141-141


82-82: Excellent API versioning implementation.

The versioned endpoint approach is well-implemented:

  • Maintains backward compatibility through the UNVERSIONED decorator
  • Provides enhanced authorization with the Or guard allowing either admin or user ownership
  • Includes proper API documentation with required headers
  • Uses consistent parameter handling across versions

This addresses the PR objective of reintroducing functionality without breaking existing endpoints.

Also applies to: 105-136


94-103: Correct parameter handling.

The service method call has been updated to pass the correct parameters. Based on the past review comments, the removal of the unused orgId parameter is appropriate.

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 (2)
apps/api/v2/swagger/documentation.json (2)

5083-5096: IDs should be declared as integer, not number
orgId and teamId are primary-key IDs; defining them as floating-point capable number invites client coercion issues.
This exact concern was raised earlier and remains unresolved.


23555-23580: Identifier fields and teamMemberIds requirement issue persist
responseId and items inside teamMemberIds are still typed as number; they should be integer.
Additionally, teamMemberIds remains required, yet a queued response may legitimately have none, risking validation failures.

🧹 Nitpick comments (3)
apps/api/v2/swagger/documentation.json (3)

5137-5145: Use integer for the duration query parameter
Duration is expressed in whole minutes (e.g., 60). Declare its schema as:

-              "type": "number"
+              "type": "integer"

to convey discrete values and improve generator fidelity.


5182-5191: Add a meaningful 201 response description
The description field is empty. A short sentence (e.g., “Routing form response successfully created.”) improves generated client docs and Swagger UI clarity.


23610-23613: eventTypeId should be integer
As an ID, this field should use integer to match database type and avoid float coercion.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e293fc1 and 88193c4.

📒 Files selected for processing (3)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts (25 hunks)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts (6 hunks)
  • apps/api/v2/swagger/documentation.json (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.ts
  • apps/api/v2/src/modules/organizations/routing-forms/controllers/organizations-routing-forms-responses.controller.e2e-spec.ts
🧰 Additional context used
🧠 Learnings (1)
apps/api/v2/swagger/documentation.json (2)

undefined

<retrieved_learning>
Learnt from: alishaz-polymath
PR: #22304
File: packages/prisma/schema.prisma:1068-1071
Timestamp: 2025-07-16T05:10:22.863Z
Learning: In PR #22304 for Cal.com private link expiration features, the maxUsageCount field was intentionally set to default to 1 (non-nullable) as a breaking change, making all existing private links single-use after migration. This was a deliberate design decision by alishaz-polymath.
</retrieved_learning>

<retrieved_learning>
Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
</retrieved_learning>

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Security Check

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

♻️ Duplicate comments (3)
apps/api/v2/swagger/documentation.json (3)

5083-5097: Path IDs should be declared as integer, not number

Using the floating-point number type for discrete identifiers (orgId, teamId) can cause client generators to coerce them into floats. Stick to integer to avoid precision loss and validation issues.

-              "type": "number"
+              "type": "integer"

24962-24978: ID fields and teamMemberIds array still use number & remain required

Previous feedback flagged the same issue:

  1. responseId and items in teamMemberIds should be integer.
  2. teamMemberIds can be empty when a response is queued; making it required may cause validation failures.
-          "responseId": {
-            "type": "number",
+          "responseId": {
+            "type": "integer",
 ...
-              "type": "number"
+              "type": "integer"
@@
-        "required": [
-          "teamMemberIds"
-        ]
+        "required": []

25007-25010: eventTypeId should be an integer

Identifiers are discrete and should not be expressed as floating-point numbers.

-          "eventTypeId": {
-            "type": "number",
+          "eventTypeId": {
+            "type": "integer",
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 88193c4 and 1c169f9.

📒 Files selected for processing (1)
  • apps/api/v2/swagger/documentation.json (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
apps/api/v2/swagger/documentation.json (2)

undefined

<retrieved_learning>
Learnt from: alishaz-polymath
PR: #22304
File: packages/prisma/schema.prisma:1068-1071
Timestamp: 2025-07-16T05:10:22.863Z
Learning: In PR #22304 for Cal.com private link expiration features, the maxUsageCount field was intentionally set to default to 1 (non-nullable) as a breaking change, making all existing private links single-use after migration. This was a deliberate design decision by alishaz-polymath.
</retrieved_learning>

<retrieved_learning>
Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.341Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
</retrieved_learning>

⏰ 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: Security Check

@hariombalhara hariombalhara force-pushed the add-teams-routing-forms-response-record-endpoint-back branch from 1c169f9 to c27e244 Compare July 16, 2025 16:01
@hariombalhara hariombalhara force-pushed the add-teams-routing-forms-response-record-endpoint-back branch from c27e244 to 84e2690 Compare July 16, 2025 16:07
@hariombalhara hariombalhara merged commit ec58e59 into main Jul 17, 2025
42 checks passed
@hariombalhara hariombalhara deleted the add-teams-routing-forms-response-record-endpoint-back branch July 17, 2025 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api area: API, enterprise API, access token, OAuth core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO High priority Created by Linear-GitHub Sync ready-for-e2e routing-forms area: routing forms, routing, forms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants