perf: import enums from @calcom/prisma/enums#23582
Conversation
WalkthroughThis PR changes many enum imports across the codebase to import from Possibly related PRs
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
@calcom/prisma/enums
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (13)
apps/api/v2/src/modules/webhooks/outputs/webhook.output.ts (1)
52-55: Fix type/decorators mismatch ondata.datais typedstringbut decorated as nestedWebhookOutputDto, which breaks transformation/validation and misleads Swagger. Either remove@ValidateNested()/@Type()and keepdata!: string;or change todata!: WebhookOutputDto[]with@IsArray(),@ValidateNested({ each: true })and@Type(() => WebhookOutputDto).apps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.ts (1)
10-12: Fix validator onaccepted(should be boolean).Using
@IsString()on a boolean field causes incorrect validation and docs.- @IsString() + @IsBoolean() @Expose() readonly accepted!: boolean;apps/api/v2/src/modules/workflows/inputs/workflow-trigger.input.ts (1)
78-84: Broken validation: IsIn receives a nested array.@isin([WORKFLOW_TRIGGER_TYPES]) passes a single element array whose only element is the array of values, causing validation to fail.
Apply:
- @IsIn([WORKFLOW_TRIGGER_TYPES]) + @IsIn(WORKFLOW_TRIGGER_TYPES)apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts (1)
205-215: Spread null bug: getRoutingFormData returns null but callers spread the result.transformInputCreateBooking/transformInputCreateRecurringBooking do
{ ..., ...this.getRoutingFormData(routing) }. Spreading null throws at runtime.Apply one of:
- if (!routing) return null; + if (!routing) return {};or change call sites to
...(this.getRoutingFormData(routing) ?? {}).apps/api/v2/src/modules/workflows/inputs/workflow-step.input.ts (2)
59-66: Bug: TEMPLATES_TO_ENUM mapping is inverted for REMINDER.First entry maps enum→string instead of string→enum; causes incorrect lookup for REMINDER.
-export const TEMPLATES_TO_ENUM = { - [WorkflowTemplates.REMINDER]: REMINDER, - [CUSTOM]: WorkflowTemplates.CUSTOM, - [RESCHEDULED]: WorkflowTemplates.RESCHEDULED, - [CANCELLED]: WorkflowTemplates.CANCELLED, - [COMPLETED]: WorkflowTemplates.COMPLETED, - [RATING]: WorkflowTemplates.RATING, -} as const; +export const TEMPLATES_TO_ENUM = { + [REMINDER]: WorkflowTemplates.REMINDER, + [CUSTOM]: WorkflowTemplates.CUSTOM, + [RESCHEDULED]: WorkflowTemplates.RESCHEDULED, + [CANCELLED]: WorkflowTemplates.CANCELLED, + [COMPLETED]: WorkflowTemplates.COMPLETED, + [RATING]: WorkflowTemplates.RATING, +} as const;
79-79: Bug: HOST constant value looks wrong.
"const"should be"host"to match recipient types.-export const HOST = "const"; +export const HOST = "host";apps/api/v2/src/modules/workflows/workflows.repository.ts (1)
27-31: Prisma guideline: replace include with select.Project rule: “only select data you need; never use include.” Please migrate these queries to
selectwith explicit fields.Example pattern:
- include: { - steps: true, - activeOn: { select: { eventTypeId: true } }, - }, + select: { + // enumerate needed Workflow scalar fields explicitly + id: true, + name: true, + trigger: true, + time: true, + timeUnit: true, + teamId: true, + steps: true, + activeOn: { select: { eventTypeId: true } }, + },Repeat similarly for
findManyandcreate(replaceincludewithselect). If helpful, I can draft aworkflowSelectconstant to centralize the field list.Also applies to: 41-44, 61-61
apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts (5)
203-209: Don’t combine void with await — it blocks unnecessarily.This defeats fire-and-forget and can slow the booking response.
- void (await this.billingService.increaseUsageByUserId(booking.userId, { + void this.billingService.increaseUsageByUserId(booking.userId, { uid: booking.uid, startTime: booking.startTime, fromReschedule: booking.fromReschedule, - })); + });
256-258: Same here: remove await when intentionally ignoring the result.- void (await this.billingService.cancelUsageByBookingUid(res.bookingUid)); + void this.billingService.cancelUsageByBookingUid(res.bookingUid);
329-336: Avoid async Array.forEach — rejections become unhandled.Use for...of with fire-and-forget or Promise.allSettled.
- createdBookings.forEach(async (booking) => { - if (booking.userId && booking.uid && booking.startTime) { - void (await this.billingService.increaseUsageByUserId(booking.userId, { - uid: booking.uid, - startTime: booking.startTime, - })); - } - }); + for (const booking of createdBookings) { + if (booking.userId && booking.uid && booking.startTime) { + void this.billingService.increaseUsageByUserId(booking.userId, { + uid: booking.uid, + startTime: booking.startTime, + }); + } + }
367-371: Same pattern in instant booking.- void (await this.billingService.increaseUsageByUserId(instantMeeting.userId, { + void this.billingService.increaseUsageByUserId(instantMeeting.userId, { uid: instantMeeting.bookingUid, startTime: now, - })); + });
387-391: Replaceapi.apiKeyPrefixwith the definedapi.keyPrefixfor both prefix check and stripping
In apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts (and similarly in the 2024-08-13 service),api.apiKeyPrefixdoesn’t exist in your config—useapi.keyPrefixconsistently:- if (isApiKey(bearerToken, this.config.get<string>("api.apiKeyPrefix") ?? "cal_")) { - const strippedApiKey = stripApiKey(bearerToken, this.config.get<string>("api.keyPrefix")); + const keyPrefix = this.config.get<string>("api.keyPrefix"); + if (isApiKey(bearerToken, keyPrefix)) { + const strippedApiKey = stripApiKey(bearerToken, keyPrefix);apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts (1)
85-87: Avoid logging PII by stringifying request bodiesBodies can contain personal data. Log minimal, non-sensitive context instead.
Apply:
- this.logger.log( - `Creating user with data: ${JSON.stringify(body, null, 2)} for OAuth Client with ID ${oAuthClientId}` - ); + this.logger.log(`Creating managed user for OAuth Client ${oAuthClientId}`);- this.logger.log(`Updating user with ID ${userId}: ${JSON.stringify(body, null, 2)}`); + this.logger.log(`Updating managed user ${userId}`);- this.logger.log(`Forcing new access tokens for managed user with ID ${userId}`); + this.logger.log(`Forcing new access tokens for managed user ${userId}`);Also applies to: 133-133, 173-173
🧹 Nitpick comments (28)
apps/api/v2/src/modules/webhooks/outputs/webhook.output.ts (1)
41-44: Verify exposure ofsecretin responses.
secretis marked with@Expose()and will be serialized. If this is a credential, avoid returning it except at creation time (and consider writeOnly in docs).If
secretmust not be in responses:- @IsString() - @Expose() - readonly secret?: string; + // Intentionally not exposed in responses + @IsString() + readonly secret?: string;Or document-only visibility:
+import { ApiPropertyOptional } from "@nestjs/swagger"; ... - @IsString() - @Expose() - readonly secret?: string; + @IsString() + @ApiPropertyOptional({ writeOnly: true }) + readonly secret?: string;apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.ts (2)
74-76: Avoid logging full request bodies; scrub sensitive fields.
JSON.stringify(body)may include PII or secrets in some inputs. Prefer structured, minimal logs with redaction.- this.logger.log( - `For organisation ${organizationId} creating OAuth Client with data: ${JSON.stringify(body)}` - ); + const { name, clientType } = body as any; + this.logger.log( + `For organization ${organizationId} creating OAuth Client with data: ${JSON.stringify({ name, clientType })}` + );Please confirm
CreateOAuthClientInputandUpdateOAuthClientInputnever carry secrets; if they can, let’s mask common fields (e.g., clientSecret, secret, privateKey).
145-146: Same logging concern on update path.Apply the same redaction pattern here.
- this.logger.log(`For client ${clientId} updating OAuth Client with data: ${JSON.stringify(body)}`); + const { name, clientType } = body as any; + this.logger.log( + `For client ${clientId} updating OAuth Client with data: ${JSON.stringify({ name, clientType })}` + );apps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.ts (2)
28-32: Array DTO validation: validate elements and mark as array.For arrays, prefer
@IsArray()and@ValidateNested({ each: true })so each element is validated.@Expose() - @ValidateNested() + @IsArray() + @ValidateNested({ each: true }) @Type(() => OrgTeamOutputDto) data!: OrgTeamOutputDto[];
39-43: Same array DTO validation improvement.Apply
@IsArray()and@ValidateNested({ each: true })here as well.@Expose() - @ValidateNested() + @IsArray() + @ValidateNested({ each: true }) @Type(() => OrgTeamOutputDto) data!: OrgTeamOutputDto[];apps/api/v2/src/modules/teams/teams/outputs/teams/get-teams.output.ts (1)
9-11: Prefer IsIn over IsEnum for literal values array.status validates against two literals, not a TS/JS enum object. Use IsIn for clarity.
Apply:
- @IsEnum([SUCCESS_STATUS, ERROR_STATUS]) + @IsIn([SUCCESS_STATUS, ERROR_STATUS])apps/api/v2/src/modules/organizations/event-types/services/output.service.ts (3)
16-16: Use enum constants instead of string literals for SchedulingType.Mixing enum members and raw strings is brittle. Compare against
SchedulingType.*consistently.Apply:
- const hosts = - databaseEventType.schedulingType === "MANAGED" + const hosts = + databaseEventType.schedulingType === SchedulingType.MANAGED ? await this.getManagedEventTypeHosts(databaseEventType.id) : await this.getNonManagedEventTypeHosts(databaseEventType.hosts, databaseEventType.schedulingType)- if (schedulingType === "ROUND_ROBIN") { + if (schedulingType === SchedulingType.ROUND_ROBIN) {Also applies to: 106-109, 175-176
47-47: Duplicate key in Input Pick list.
"lockTimeZoneToggleOnBookingPage"appears twice; drop the duplicate to avoid confusion.| "requiresBookerEmailVerification" | "hideCalendarNotes" - | "lockTimeZoneToggleOnBookingPage" | "eventTypeColor"Also applies to: 75-75
147-161: Avoid N+1 queries when building managed hosts.This loop issues 1 query per child. Batch-fetch by IDs once.
async getManagedEventTypeHosts(eventTypeId: number) { const children = await this.teamsEventTypesRepository.getEventTypeChildren(eventTypeId); - const transformedHosts: TeamEventTypeResponseHost[] = []; - for (const child of children) { - if (child.userId) { - const user = await this.usersRepository.findById(child.userId); - transformedHosts.push({ - userId: child.userId, - name: user?.name || "", - username: user?.username || "", - avatarUrl: user?.avatarUrl, - }); - } - } - return transformedHosts; + const userIds = [...new Set(children.map((c) => c.userId).filter(Boolean) as number[])]; + const users = await this.usersRepository.findByIds(userIds); + const usersById = new Map(users.map((u) => [u.id, u])); + return children + .filter((c): c is { userId: number } => !!c.userId) + .map((c) => { + const u = usersById.get(c.userId); + return { + userId: c.userId, + name: u?.name || "", + username: u?.username || "", + avatarUrl: u?.avatarUrl, + }; + }); }apps/api/v2/src/modules/workflows/inputs/workflow-step.input.ts (1)
171-178: Swagger examples should be numbers, not strings.
verifiedEmailId/verifiedPhoneIdare numbers; fix example values.- example: "31214", + example: 31214, ... - example: "3243434", + example: 3243434, ... - example: "3243434", + example: 3243434,Also applies to: 225-234, 274-283
apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts (1)
13-15: Prefer enum for role instead of raw string.Use
MembershipRole.MEMBERfor consistency with the enums refactor.-import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries"; -import { CreationSource } from "@calcom/prisma/enums"; +import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries"; +import { CreationSource, MembershipRole } from "@calcom/prisma/enums"; ... - role: "MEMBER", + role: MembershipRole.MEMBER,Also applies to: 51-53
apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts (1)
512-542: Remove unused private methodcreateNextApiRecurringBookingRequest. No calls were found in the codebase, so this dead code should be removed to prevent drift.apps/api/v2/src/modules/organizations/teams/memberships/inputs/update-organization-team-membership.input.ts (1)
12-15: Drive Swagger enum from source enum to prevent drift.- @ApiPropertyOptional({ enum: ["MEMBER", "OWNER", "ADMIN"] }) + @ApiPropertyOptional({ enum: MembershipRole, enumName: "MembershipRole" })apps/api/v2/src/modules/teams/memberships/outputs/team-membership.output.ts (1)
74-77: Use IsEnum and enum from source to keep docs in sync.- @IsString() - @ApiProperty({ enum: ["MEMBER", "OWNER", "ADMIN"] }) + @IsEnum(MembershipRole) + @ApiProperty({ enum: MembershipRole, enumName: "MembershipRole" }) @Expose() readonly role!: MembershipRole;apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts (2)
33-39: Wrap create+user update in a single transaction to avoid partial writes.- async addUserToOrg(user: User, org: Team, role: MembershipRole, accepted: boolean) { - const membership = await this.prismaWriteClient.membership.create({ - data: { createdAt: new Date(), teamId: org.id, userId: user.id, role, accepted }, - }); - await this.prismaWriteClient.user.update({ where: { id: user.id }, data: { organizationId: org.id } }); - return membership; - } + async addUserToOrg(user: User, org: Team, role: MembershipRole, accepted: boolean) { + const [membership] = await this.prismaWriteClient.$transaction([ + this.prismaWriteClient.membership.create({ + data: { createdAt: new Date(), teamId: org.id, userId: user.id, role, accepted }, + }), + this.prismaWriteClient.user.update({ where: { id: user.id }, data: { organizationId: org.id } }), + ]); + return membership; + }
25-31: Prefer select to return only required fields (repo guideline).These reads return whole rows. Consider adding a minimal select for tests to speed up fixtures.
Also applies to: 41-43
apps/api/v2/src/modules/organizations/memberships/inputs/create-organization-membership.input.ts (1)
16-21: Keep Swagger enum tied to MembershipRole to avoid hard-coded lists.- @ApiProperty({ - enum: ["MEMBER", "OWNER", "ADMIN"], + @ApiProperty({ + enum: MembershipRole, + enumName: "MembershipRole", description: "If you are platform customer then managed users should only have MEMBER role.", })apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts (1)
35-35: Use a type-only import for Prisma model to avoid loading @prisma/client at runtimeThis
Userusage is purely for typing; using a value import needlessly pulls in @prisma/client.Apply:
-import { User } from "@prisma/client"; +import type { User } from "@prisma/client";Also consider whether the PR goal intends to eliminate all direct Prisma imports. If yes, re-export model types from an internal package and consume those instead.
apps/api/v2/src/modules/organizations/memberships/inputs/update-organization-membership.input.ts (1)
12-15: Keep Swagger enum in sync with the source enumUsing a hardcoded array risks drifting from MembershipRole. Prefer referencing the enum directly.
Apply:
- @ApiPropertyOptional({ enum: ["MEMBER", "OWNER", "ADMIN"] }) + @ApiPropertyOptional({ enum: MembershipRole })apps/api/v2/src/modules/webhooks/inputs/webhook.input.ts (1)
2-2: Validate subscriberUrl as a URL, not just a stringStrengthen input validation to prevent bad webhook targets.
Apply:
-import { IsArray, IsBoolean, IsEnum, IsOptional, IsString } from "class-validator"; +import { IsArray, IsBoolean, IsEnum, IsOptional, IsString, IsUrl } from "class-validator";- @IsString() + @IsUrl({ protocols: ["http", "https"] }, { message: "subscriberUrl must be a valid http(s) URL" }) @ApiProperty() subscriberUrl!: string;Also applies to: 26-29
apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-webhooks/oauth-client-webhooks.controller.ts (2)
20-21: Import Prisma model as type-only to avoid runtime cost
Webhookis used only for typing; make it a type-only import to prevent loading @prisma/client.Apply:
-import { Webhook } from "@prisma/client"; +import type { Webhook } from "@prisma/client";If the PR aims for zero direct Prisma imports, consider consuming a re-exported type from an internal package instead.
60-63: Optional: Prefer plainToInstance over deprecated plainToClassclass-transformer recommends plainToInstance. Not required for this PR, but improves forward-compatibility.
Apply:
-import { plainToClass } from "class-transformer"; +import { plainToInstance } from "class-transformer";- data: plainToClass(OAuthClientWebhookOutputDto, new WebhookOutputPipe().transform(webhook), { + data: plainToInstance(OAuthClientWebhookOutputDto, new WebhookOutputPipe().transform(webhook), { strategy: "excludeAll", }),Repeat similarly for other occurrences in this file.
Also applies to: 80-83, 92-96, 130-134
apps/api/v2/src/modules/organizations/teams/memberships/inputs/create-organization-team-membership.input.ts (1)
16-19: Use enum object in Swagger to avoid driftReplace hardcoded array with the enum object (adds reuse and prevents mismatches if roles change).
- @ApiProperty({ enum: ["MEMBER", "OWNER", "ADMIN"] }) + @ApiProperty({ enum: MembershipRole, enumName: "MembershipRole" })apps/api/v2/src/modules/organizations/attributes/index/inputs/update-organization-attribute.input.ts (1)
17-21: Name the enum in Swagger for cleaner schemaAdd enumName so the generated OpenAPI uses a named reusable schema.
- @ApiPropertyOptional({ enum: AttributeType }) + @ApiPropertyOptional({ enum: AttributeType, enumName: "AttributeType" })apps/api/v2/src/modules/teams/memberships/inputs/update-team-membership.input.ts (1)
12-15: Avoid hardcoded enum values in SwaggerPrefer referencing the enum directly so docs stay in sync if roles evolve.
- @ApiPropertyOptional({ enum: ["MEMBER", "OWNER", "ADMIN"] }) + @ApiPropertyOptional({ enum: MembershipRole, enumName: "MembershipRole" })apps/api/v2/src/modules/users/users.repository.ts (2)
78-85: Prisma: replaceincludewithselectper repo guidelineThis file uses
includein several queries. Our guideline for**/*.tsis “only select data you need; never use include”. Consider migrating these toselect. Example forfindByIdWithProfile:- return this.dbRead.prisma.user.findUnique({ - where: { id: userId }, - include: { - movedToProfile: { - include: { organization: { select: { isPlatform: true, name: true, slug: true, id: true } } }, - }, - profiles: { - include: { organization: { select: { isPlatform: true, name: true, slug: true, id: true } } }, - }, - }, - }); + return this.dbRead.prisma.user.findUnique({ + where: { id: userId }, + select: { + // add only the user fields actually needed by callers, e.g.: + id: true, + email: true, + username: true, + movedToProfile: { + select: { organization: { select: { isPlatform: true, name: true, slug: true, id: true } } }, + }, + profiles: { + select: { organization: { select: { isPlatform: true, name: true, slug: true, id: true } } }, + }, + }, + });Same pattern applies to the other marked blocks.
Do callers rely on full
Usershape here? If yes, consider defining a constrained output type and migrating incrementally.Also applies to: 99-106, 117-119, 138-141, 158-165, 288-291
260-264: No-op setter—either remove or implement
formatInputcurrently re-assignsweekStartto itself. Drop it or implement the intended normalization.- formatInput(userInput: CreateManagedUserInput | UpdateManagedUserInput) { - if (userInput.weekStart) { - userInput.weekStart = userInput.weekStart; - } - } + formatInput(_userInput: CreateManagedUserInput | UpdateManagedUserInput) { + // Intentionally left blank – add normalization when requirements are defined. + }apps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts (1)
9-9: Use type-only imports for Prisma models in testsThese are used purely for typing; avoid creating runtime bindings.
-import { User } from "@prisma/client"; +import type { User } from "@prisma/client"; @@ -import { Team } from "@calcom/prisma/client"; +import type { Team } from "@calcom/prisma/client";Also applies to: 35-35
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (32)
apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts(1 hunks)apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts(1 hunks)apps/api/v2/src/lib/roles/constants.ts(1 hunks)apps/api/v2/src/modules/auth/decorators/roles/membership-roles.decorator.ts(1 hunks)apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts(1 hunks)apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-webhooks/oauth-client-webhooks.controller.ts(1 hunks)apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.ts(1 hunks)apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts(1 hunks)apps/api/v2/src/modules/organizations/attributes/index/inputs/create-organization-attribute.input.ts(1 hunks)apps/api/v2/src/modules/organizations/attributes/index/inputs/update-organization-attribute.input.ts(1 hunks)apps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts(2 hunks)apps/api/v2/src/modules/organizations/event-types/services/output.service.ts(1 hunks)apps/api/v2/src/modules/organizations/memberships/inputs/create-organization-membership.input.ts(1 hunks)apps/api/v2/src/modules/organizations/memberships/inputs/update-organization-membership.input.ts(1 hunks)apps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.ts(1 hunks)apps/api/v2/src/modules/organizations/teams/memberships/inputs/create-organization-team-membership.input.ts(1 hunks)apps/api/v2/src/modules/organizations/teams/memberships/inputs/update-organization-team-membership.input.ts(1 hunks)apps/api/v2/src/modules/organizations/users/index/inputs/create-organization-user.input.ts(1 hunks)apps/api/v2/src/modules/organizations/users/index/services/organizations-users-service.ts(1 hunks)apps/api/v2/src/modules/teams/memberships/inputs/create-team-membership.input.ts(1 hunks)apps/api/v2/src/modules/teams/memberships/inputs/update-team-membership.input.ts(1 hunks)apps/api/v2/src/modules/teams/memberships/outputs/team-membership.output.ts(1 hunks)apps/api/v2/src/modules/teams/teams/outputs/teams/get-team.output.ts(1 hunks)apps/api/v2/src/modules/teams/teams/outputs/teams/get-teams.output.ts(1 hunks)apps/api/v2/src/modules/teams/teams/outputs/teams/update-team.output.ts(1 hunks)apps/api/v2/src/modules/users/users.repository.ts(1 hunks)apps/api/v2/src/modules/webhooks/inputs/webhook.input.ts(1 hunks)apps/api/v2/src/modules/webhooks/outputs/webhook.output.ts(1 hunks)apps/api/v2/src/modules/workflows/inputs/workflow-step.input.ts(1 hunks)apps/api/v2/src/modules/workflows/inputs/workflow-trigger.input.ts(1 hunks)apps/api/v2/src/modules/workflows/workflows.repository.ts(1 hunks)apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Avoid dot-suffixes like
.service.tsor.repository.tsfor new files; reserve.test.ts,.spec.ts,.types.tsfor their specific purposes
Files:
apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.tsapps/api/v2/src/modules/workflows/workflows.repository.tsapps/api/v2/src/ee/bookings/2024-08-13/services/input.service.tsapps/api/v2/src/modules/organizations/event-types/services/output.service.tsapps/api/v2/src/modules/users/users.repository.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.tsapps/api/v2/src/modules/webhooks/outputs/webhook.output.tsapps/api/v2/src/modules/organizations/attributes/index/inputs/update-organization-attribute.input.tsapps/api/v2/src/modules/teams/teams/outputs/teams/get-teams.output.tsapps/api/v2/src/modules/workflows/inputs/workflow-step.input.tsapps/api/v2/src/modules/teams/teams/outputs/teams/get-team.output.tsapps/api/v2/src/modules/webhooks/inputs/webhook.input.tsapps/api/v2/src/lib/roles/constants.tsapps/api/v2/src/modules/teams/memberships/inputs/create-team-membership.input.tsapps/api/v2/src/modules/workflows/inputs/workflow-trigger.input.tsapps/api/v2/src/modules/workflows/workflows.repository.tsapps/api/v2/src/modules/organizations/attributes/index/inputs/create-organization-attribute.input.tsapps/api/v2/src/modules/organizations/memberships/inputs/update-organization-membership.input.tsapps/api/v2/src/modules/organizations/teams/memberships/inputs/update-organization-team-membership.input.tsapps/api/v2/src/modules/auth/decorators/roles/membership-roles.decorator.tsapps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.tsapps/api/v2/src/ee/bookings/2024-08-13/services/input.service.tsapps/api/v2/src/modules/organizations/event-types/services/output.service.tsapps/api/v2/src/modules/teams/teams/outputs/teams/update-team.output.tsapps/api/v2/src/modules/organizations/users/index/services/organizations-users-service.tsapps/api/v2/src/modules/organizations/users/index/inputs/create-organization-user.input.tsapps/api/v2/src/modules/organizations/memberships/inputs/create-organization-membership.input.tsapps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.tsapps/api/v2/src/modules/teams/memberships/inputs/update-team-membership.input.tsapps/api/v2/src/modules/teams/memberships/outputs/team-membership.output.tsapps/api/v2/src/modules/users/users.repository.tsapps/api/v2/src/modules/organizations/teams/memberships/inputs/create-organization-team-membership.input.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.tsapps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.tsapps/api/v2/test/fixtures/repository/membership.repository.fixture.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-client-webhooks/oauth-client-webhooks.controller.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.tsapps/api/v2/src/modules/webhooks/outputs/webhook.output.tsapps/api/v2/src/modules/organizations/attributes/index/inputs/update-organization-attribute.input.tsapps/api/v2/src/modules/teams/teams/outputs/teams/get-teams.output.tsapps/api/v2/src/modules/workflows/inputs/workflow-step.input.tsapps/api/v2/src/modules/teams/teams/outputs/teams/get-team.output.tsapps/api/v2/src/modules/webhooks/inputs/webhook.input.tsapps/api/v2/src/lib/roles/constants.tsapps/api/v2/src/modules/teams/memberships/inputs/create-team-membership.input.tsapps/api/v2/src/modules/workflows/inputs/workflow-trigger.input.tsapps/api/v2/src/modules/workflows/workflows.repository.tsapps/api/v2/src/modules/organizations/attributes/index/inputs/create-organization-attribute.input.tsapps/api/v2/src/modules/organizations/memberships/inputs/update-organization-membership.input.tsapps/api/v2/src/modules/organizations/teams/memberships/inputs/update-organization-team-membership.input.tsapps/api/v2/src/modules/auth/decorators/roles/membership-roles.decorator.tsapps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.tsapps/api/v2/src/ee/bookings/2024-08-13/services/input.service.tsapps/api/v2/src/modules/organizations/event-types/services/output.service.tsapps/api/v2/src/modules/teams/teams/outputs/teams/update-team.output.tsapps/api/v2/src/modules/organizations/users/index/services/organizations-users-service.tsapps/api/v2/src/modules/organizations/users/index/inputs/create-organization-user.input.tsapps/api/v2/src/modules/organizations/memberships/inputs/create-organization-membership.input.tsapps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.tsapps/api/v2/src/modules/teams/memberships/inputs/update-team-membership.input.tsapps/api/v2/src/modules/teams/memberships/outputs/team-membership.output.tsapps/api/v2/src/modules/users/users.repository.tsapps/api/v2/src/modules/organizations/teams/memberships/inputs/create-organization-team-membership.input.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.tsapps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.tsapps/api/v2/test/fixtures/repository/membership.repository.fixture.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-client-webhooks/oauth-client-webhooks.controller.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.tsapps/api/v2/src/modules/webhooks/outputs/webhook.output.tsapps/api/v2/src/modules/organizations/attributes/index/inputs/update-organization-attribute.input.tsapps/api/v2/src/modules/teams/teams/outputs/teams/get-teams.output.tsapps/api/v2/src/modules/workflows/inputs/workflow-step.input.tsapps/api/v2/src/modules/teams/teams/outputs/teams/get-team.output.tsapps/api/v2/src/modules/webhooks/inputs/webhook.input.tsapps/api/v2/src/lib/roles/constants.tsapps/api/v2/src/modules/teams/memberships/inputs/create-team-membership.input.tsapps/api/v2/src/modules/workflows/inputs/workflow-trigger.input.tsapps/api/v2/src/modules/workflows/workflows.repository.tsapps/api/v2/src/modules/organizations/attributes/index/inputs/create-organization-attribute.input.tsapps/api/v2/src/modules/organizations/memberships/inputs/update-organization-membership.input.tsapps/api/v2/src/modules/organizations/teams/memberships/inputs/update-organization-team-membership.input.tsapps/api/v2/src/modules/auth/decorators/roles/membership-roles.decorator.tsapps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.tsapps/api/v2/src/ee/bookings/2024-08-13/services/input.service.tsapps/api/v2/src/modules/organizations/event-types/services/output.service.tsapps/api/v2/src/modules/teams/teams/outputs/teams/update-team.output.tsapps/api/v2/src/modules/organizations/users/index/services/organizations-users-service.tsapps/api/v2/src/modules/organizations/users/index/inputs/create-organization-user.input.tsapps/api/v2/src/modules/organizations/memberships/inputs/create-organization-membership.input.tsapps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.tsapps/api/v2/src/modules/teams/memberships/inputs/update-team-membership.input.tsapps/api/v2/src/modules/teams/memberships/outputs/team-membership.output.tsapps/api/v2/src/modules/users/users.repository.tsapps/api/v2/src/modules/organizations/teams/memberships/inputs/create-organization-team-membership.input.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.tsapps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.tsapps/api/v2/test/fixtures/repository/membership.repository.fixture.tsapps/api/v2/src/modules/oauth-clients/controllers/oauth-client-webhooks/oauth-client-webhooks.controller.ts
🧠 Learnings (4)
📚 Learning: 2025-08-21T12:28:42.018Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/factory/WebhookPayloadFactory.ts:274-282
Timestamp: 2025-08-21T12:28:42.018Z
Learning: In webhook DTOs in packages/features/webhooks/lib/dto/types.ts, the booking fields are restricted structures containing only specific fields (id, eventTypeId, userId, and sometimes additional fields like startTime or smsReminderNumber) rather than full database booking objects, so there are no security or PII leakage concerns when using these booking objects in webhook payloads.
Applied to files:
apps/api/v2/src/modules/webhooks/outputs/webhook.output.tsapps/api/v2/src/modules/webhooks/inputs/webhook.input.ts
📚 Learning: 2025-08-26T20:09:17.089Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/features/ee/workflows/components/WorkflowStepContainer.tsx:641-649
Timestamp: 2025-08-26T20:09:17.089Z
Learning: In packages/features/ee/workflows/components/WorkflowStepContainer.tsx, Cal.AI actions are intentionally filtered out/hidden for organization workflows when props.isOrganization is true. This restriction is by design per maintainer Udit-takkar in PR #22995, despite the broader goal of enabling Cal.AI self-serve.
Applied to files:
apps/api/v2/src/modules/workflows/inputs/workflow-step.input.ts
📚 Learning: 2025-08-27T13:32:46.887Z
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
Applied to files:
apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.tsapps/api/v2/src/modules/organizations/event-types/services/output.service.tsapps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
Applied to files:
apps/api/v2/src/modules/organizations/event-types/services/output.service.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (33)
apps/api/v2/src/modules/webhooks/outputs/webhook.output.ts (1)
6-6: Enum import source change looks good.Switching WebhookTriggerEvents to @calcom/prisma/enums aligns with the PR objective and avoids pulling from @prisma/client directly.
apps/api/v2/src/modules/oauth-clients/controllers/oauth-clients/oauth-clients.controller.ts (1)
43-43: Enum import source change looks good.Using MembershipRole from @calcom/prisma/enums is consistent with the refactor goal.
apps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.ts (1)
7-7: Enum import source change looks good.Moving MembershipRole to @calcom/prisma/enums aligns with the refactor scope.
apps/api/v2/src/modules/teams/teams/outputs/teams/get-team.output.ts (1)
3-3: Import cleanup looks good.Removing unused imports keeps the DTO lean.
apps/api/v2/src/modules/teams/teams/outputs/teams/update-team.output.ts (1)
3-3: Import cleanup looks good.Same as above—good tidy-up.
apps/api/v2/src/lib/roles/constants.ts (1)
1-1: Enum import migration looks good; confirm runtime enum availability.These constants rely on MembershipRole values at runtime (template strings). Ensure @calcom/prisma/enums exports concrete runtime values (not type-only or const enums), otherwise ORG_/TEAM_ strings will become "undefined".
If unsure, quickly assert at runtime:
// e.g., in a Jest test or ts-node REPL expect(typeof MembershipRole.OWNER).toBe("string");apps/api/v2/src/modules/teams/teams/outputs/teams/get-teams.output.ts (1)
3-3: Import cleanup LGTM.Unused imports removed; keeps the file lean.
apps/api/v2/src/modules/workflows/inputs/workflow-trigger.input.ts (1)
5-6: Enum import migration LGTM.Matches the PR goal; no functional changes implied.
apps/api/v2/src/modules/organizations/attributes/index/inputs/create-organization-attribute.input.ts (1)
14-15: Enum import migration looks correct; verify runtime enum for validators/Swagger.Both @IsEnum(AttributeType) and ApiProperty({ enum: AttributeType }) require a runtime object. Confirm @calcom/prisma/enums exposes AttributeType at runtime.
Quick check:
expect(AttributeType && typeof AttributeType).toBe("object"); expect(Object.values(AttributeType).length).toBeGreaterThan(0);apps/api/v2/src/ee/bookings/2024-08-13/services/input.service.ts (3)
48-49: Enum import migration LGTM.CreationSource moved to @calcom/prisma/enums; usages remain the same.
675-683: Possible config key mismatch for API key prefix.Using api.apiKeyPrefix in isApiKey but api.keyPrefix in stripApiKey can desync behavior if keys differ.
Consider aligning keys:
- const strippedApiKey = stripApiKey(bearerToken, this.config.get<string>("api.keyPrefix")); + const strippedApiKey = stripApiKey(bearerToken, this.config.get<string>("api.apiKeyPrefix"));Verify against your actual config schema.
510-549: Consistency: Ensure CreationSource is set for reschedules.You set creationSource on create/reschedule paths above; confirm downstream expects it (analytics, auditing). Current code sets it correctly in both OAuth and non-OAuth branches—good; just calling it out for visibility.
apps/api/v2/src/modules/teams/memberships/inputs/create-team-membership.input.ts (1)
4-4: LGTM: enum import source migrated correctly.No behavior change; Swagger metadata still matches the known roles.
apps/api/v2/src/modules/oauth-clients/services/oauth-clients-users.service.ts (1)
15-15: LGTM: CreationSource import moved to enums.No functional change.
apps/api/v2/src/ee/bookings/2024-04-15/controllers/bookings.controller.ts (1)
67-68: Enum import migration looks good.Importing CreationSource from @calcom/prisma/enums matches the PR goal.
apps/api/v2/src/modules/organizations/teams/memberships/inputs/update-organization-team-membership.input.ts (1)
4-5: Enum import migration LGTM.apps/api/v2/src/modules/teams/memberships/outputs/team-membership.output.ts (1)
5-6: Enum import migration LGTM.apps/api/v2/test/fixtures/repository/membership.repository.fixture.ts (1)
6-7: Enum import migration LGTM (tests).apps/api/v2/src/modules/organizations/memberships/inputs/create-organization-membership.input.ts (1)
4-5: Enum import migration LGTM.apps/api/v2/src/modules/organizations/users/index/inputs/create-organization-user.input.ts (1)
5-5: Enum source migration looks goodImporting MembershipRole from @calcom/prisma/enums aligns with the PR goal and keeps Swagger decorators/type guards intact.
apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller.ts (1)
38-38: Enum import migrated correctlyMembershipRole now comes from @calcom/prisma/enums. Matches project direction.
apps/api/v2/src/modules/organizations/memberships/inputs/update-organization-membership.input.ts (1)
4-4: Enum import source updated correctlySwitch to @calcom/prisma/enums is consistent with the refactor plan.
apps/api/v2/src/modules/webhooks/inputs/webhook.input.ts (1)
4-4: Enum import source updated correctlyWebhookTriggerEvents now from @calcom/prisma/enums; consistent with the migration.
apps/api/v2/src/modules/oauth-clients/controllers/oauth-client-webhooks/oauth-client-webhooks.controller.ts (1)
25-25: Enum import migration acknowledgedMembershipRole moved to @calcom/prisma/enums; matches the repo-wide refactor.
apps/api/v2/src/modules/organizations/teams/memberships/inputs/create-organization-team-membership.input.ts (1)
4-4: Enum import migration LGTMImporting MembershipRole from @calcom/prisma/enums matches the PR goal. No functional changes.
apps/api/v2/src/modules/organizations/attributes/index/inputs/update-organization-attribute.input.ts (1)
4-4: Enum import migration LGTMAttributeType now sourced from @calcom/prisma/enums.
apps/api/v2/src/modules/teams/memberships/inputs/update-team-membership.input.ts (1)
1-1: Imports update LGTMSwitch to ApiPropertyOptional and enum import from @calcom/prisma/enums aligns with the refactor.
Also applies to: 4-4
apps/api/v2/src/modules/organizations/users/index/services/organizations-users-service.ts (1)
7-7: Enum source split LGTM — no leftover @prisma/client enum imports detected
Search forMembershipRole,CreationSource,AttributeType,SchedulingType,WebhookTriggerEvents,WorkflowActions,WorkflowTemplates, andTimeUnitimports from@prisma/clientreturned no results. Please manually confirm that theCreationSourceenum values in@calcom/prisma/enumsexactly match the originals from@prisma/clientto avoid DB mismatches, and audit Swagger decorators for any hard-coded enum arrays.apps/api/v2/src/modules/auth/decorators/roles/membership-roles.decorator.ts (1)
3-5: Decorator type import migration LGTMType source updated; no runtime impact on Reflector decorator.
apps/api/v2/src/modules/users/users.repository.ts (2)
8-8: Enum import migration LGTMMoving
CreationSourceto@calcom/prisma/enumsis correct and within PR scope.
35-36: NoCreationSourceimports from@prisma/clientremain
All occurrences now import from@calcom/prisma/enums.apps/api/v2/src/modules/organizations/event-types/organizations-event-types.e2e-spec.ts (2)
36-36: ImportingSchedulingTypefrom enums is correctMatches the refactor goal to stop sourcing enums from
@prisma/client.
9-9: No Prisma enum imports found in API tests Verified that there are no remaining imports of enums from@prisma/clientinapps/api/v2tests.
apps/api/v2/src/modules/organizations/teams/index/outputs/organization-team.output.ts
Show resolved
Hide resolved
apps/api/v2/src/modules/teams/memberships/outputs/team-membership.output.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (17)
packages/app-store/routing-forms/trpc/utils.ts (1)
139-161: Fix: unawaited webhook sends and secret leakage in logs
- The map callback does not return the promise, so
promisesFormSubmittedbecomes an array ofundefinedandPromise.allwon’t await webhook sends.- Logging the entire
webhookobject risks leakingwebhook.secret.Return the promise and log only safe identifiers. Also, prefer the enum for
triggerEventto avoid drift.- const promisesFormSubmitted = webhooksFormSubmitted.map((webhook) => { - sendGenericWebhookPayload({ - secretKey: webhook.secret, - triggerEvent: "FORM_SUBMITTED", - createdAt: new Date().toISOString(), - webhook, - data: { - formId: form.id, - formName: form.name, - teamId: form.teamId, - responses: fieldResponsesByIdentifier, - }, - rootData: { - // Send responses unwrapped at root level for backwards compatibility - ...Object.entries(fieldResponsesByIdentifier).reduce((acc, [key, value]) => { - acc[key] = value.value; - return acc; - }, {} as Record<string, FormResponse[keyof FormResponse]["value"]>), - }, - }).catch((e) => { - console.error(`Error executing routing form webhook`, webhook, e); - }); - }); + const promisesFormSubmitted = webhooksFormSubmitted.map((webhook) => + sendGenericWebhookPayload({ + secretKey: webhook.secret, + triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED, + createdAt: new Date().toISOString(), + webhook, + data: { + formId: form.id, + formName: form.name, + teamId: form.teamId, + responses: fieldResponsesByIdentifier, + }, + rootData: { + // Send responses unwrapped at root level for backwards compatibility + ...Object.entries(fieldResponsesByIdentifier).reduce((acc, [key, value]) => { + acc[key] = value.value; + return acc; + }, {} as Record<string, FormResponse[keyof FormResponse]["value"]>), + }, + }).catch((e) => { + moduleLogger.error("Error executing routing form webhook", { + webhookId: webhook.id, + error: e?.message ?? e, + }); + }) + );apps/web/app/(use-page-wrapper)/auth/error/page.tsx (2)
41-49: Localize provider labels instead of hardcoded English strings.
Per guidelines, use t() for user-facing text. Replace inline strings for providerName with translated labels.Apply:
- const providerName = - provider === IdentityProvider.GOOGLE - ? "Google" - : provider === IdentityProvider.CAL - ? "Email and Password" - : provider === IdentityProvider.SAML - ? "SAML (like Okta)" - : "your original login method"; + const providerName = + provider === IdentityProvider.GOOGLE + ? t("google") + : provider === IdentityProvider.CAL + ? t("email_and_password") + : provider === IdentityProvider.SAML + ? t("saml_like_okta") + : t("your_original_login_method");
51-51: Localize “Error code” suffix.
Avoid embedding English; use a dedicated translation key.Apply:
- return t("error_during_login") + (error ? ` Error code: ${error}` : ""); + return error ? t("error_during_login_with_code", { code: error }) : t("error_during_login");apps/web/playwright/organization/organization-invitation.e2e.ts (1)
520-520: Await navigation to avoid race conditions.
Missing await can cause flakes.- signupPage.goto(inviteLink); + await signupPage.goto(inviteLink);packages/lib/server/repository/workflow.ts (3)
28-69: Prisma: prefer select over include.
Guideline: never use include; select only needed fields to reduce payload and coupling.Proposed pattern:
-const { include: includedFields } = { - include: { +const workflowSelect = { + activeOn: { + select: { + eventType: { + select: { + id: true, + title: true, + parentId: true, + _count: { select: { children: true } }, + }, + }, + }, + }, + activeOnTeams: { select: { - eventType: { - select: { - id: true, - title: true, - parentId: true, - _count: { - select: { - children: true, - }, - }, - }, - }, + team: { select: { id: true, name: true } }, }, }, - activeOnTeams: { - select: { - team: { - select: { - id: true, - name: true, - }, - }, - }, - }, steps: true, team: { select: { id: true, slug: true, name: true, members: true, logoUrl: true, isOrganization: true, }, }, -}, -} satisfies Prisma.WorkflowDefaultArgs; +} satisfies Prisma.WorkflowSelect;And switch usages:
- include: includedFields, + select: workflowSelect,Apply the above in both findMany calls below.
235-235: Switch include → select (usage 1).- include: includedFields, + select: workflowSelect,
293-293: Switch include → select (usage 2).- include: includedFields, + select: workflowSelect,packages/trpc/server/routers/viewer/admin/setSMSLockState.handler.ts (1)
19-29: Use Prismaselectto fetch only needed fields (repo guideline: avoidinclude, preferselect).Tighten reads/updates to reduce payloads and comply with our Prisma usage rule.
Apply:
- const userToUpdate = await prisma.user.findUnique({ where: { id: userId } }); + const userToUpdate = await prisma.user.findUnique({ where: { id: userId }, select: { id: true } }); - const updatedUser = await prisma.user.update({ + const updatedUser = await prisma.user.update({ where: { id: userId, }, data: { smsLockState: lock ? SMSLockState.LOCKED : SMSLockState.UNLOCKED, smsLockReviewedByAdmin: true, }, + select: { username: true }, }); - const userToUpdate = await prisma.user.findFirst({ + const userToUpdate = await prisma.user.findFirst({ where: { username, profiles: { none: {} }, }, + select: { id: true }, }); - const updatedUser = await prisma.user.update({ + const updatedUser = await prisma.user.update({ where: { id: userToUpdate.id, }, data: { smsLockState: lock ? SMSLockState.LOCKED : SMSLockState.UNLOCKED, smsLockReviewedByAdmin: true, }, + select: { username: true }, }); - const teamToUpdate = await prisma.team.findUnique({ + const teamToUpdate = await prisma.team.findUnique({ where: { id: teamId, }, + select: { id: true }, }); - const updatedTeam = await prisma.team.update({ + const updatedTeam = await prisma.team.update({ where: { id: teamId, }, data: { smsLockState: lock ? SMSLockState.LOCKED : SMSLockState.UNLOCKED, smsLockReviewedByAdmin: true, }, + select: { slug: true }, }); - const teamToUpdate = await prisma.team.findFirst({ + const teamToUpdate = await prisma.team.findFirst({ where: { slug: teamSlug, parentId: null, }, + select: { id: true }, }); - const updatedTeam = await prisma.team.update({ + const updatedTeam = await prisma.team.update({ where: { id: teamToUpdate.id, }, data: { smsLockState: lock ? SMSLockState.LOCKED : SMSLockState.UNLOCKED, smsLockReviewedByAdmin: true, }, + select: { slug: true }, });Also applies to: 39-47, 50-64, 67-82
apps/web/app/api/auth/two-factor/totp/disable/route.ts (1)
30-31: Replaceincludewithselectand narrow fields (policy: select-only).Avoid fetching entire related models; select only what’s used.
- const user = await prisma.user.findUnique({ where: { id: session.user.id }, include: { password: true } }); + const user = await prisma.user.findUnique({ + where: { id: session.user.id }, + select: { + id: true, + identityProvider: true, + twoFactorEnabled: true, + twoFactorSecret: true, + backupCodes: true, + password: { select: { hash: true } }, + }, + });packages/lib/server/repository/membership.ts (1)
318-330: Switchincludetoselectfor the availability query.Adhere to select-only rule and limit top-level fields via the existing
membershipSelect.- const memberships = await prisma.membership.findMany({ - where: { teamId }, - include: { - user: { - select: { - credentials: { - select: credentialForCalendarServiceSelect, - }, // needed for getUserAvailability - ...availabilityUserSelect, - }, - }, - }, - }); + const memberships = await prisma.membership.findMany({ + where: { teamId }, + select: { + ...membershipSelect, + user: { + select: { + credentials: { select: credentialForCalendarServiceSelect }, // needed for getUserAvailability + ...availabilityUserSelect, + }, + }, + }, + });apps/web/components/apps/installation/EventTypeConferencingAppSettings.tsx (1)
36-48: Fix early return:prefillLocationonly inspects the first group
return res;inside the outer loop short-circuits scanning remaining groups. Move return outside, or flatten before searching.- const prefillLocation = useMemo(() => { - let res: SingleValueLocationOption | undefined = undefined; - for (const item of eventType?.locationOptions || []) { - for (const option of item.options) { - if (option.slug === slug) { - res = { - ...option, - }; - } - } - return res; - } - }, [slug, eventType?.locationOptions]); + const prefillLocation = useMemo(() => { + const options = (eventType?.locationOptions ?? []).flatMap((g) => g.options); + const match = options.find((o) => o.slug === slug); + return match ? { ...match } : undefined; + }, [slug, eventType?.locationOptions]);packages/trpc/server/routers/viewer/attributes/toggleActive.handler.ts (2)
24-26: Fix grammar in user-facing error message (“a part” vs “apart”).Current copy reads awkwardly to users.
- message: "You need to be apart of an organization to use this feature", + message: "You need to be a part of an organization to use this feature",
40-42: Fix grammar in user-facing error message.Same issue as above.
- message: "You need to be apart of this organization to use this feature", + message: "You need to be a part of this organization to use this feature",packages/trpc/server/routers/viewer/attributes/edit.handler.ts (2)
24-27: Fix grammar in user-facing message (“a part” vs “apart”).- message: "You need to be apart of an organization to use this feature", + message: "You need to be a part of an organization to use this feature",
41-44: Fix grammar in user-facing message.- message: "You need to be apart of this organization to use this feature", + message: "You need to be a part of this organization to use this feature",apps/web/lib/pages/auth/verify-email.ts (1)
48-52: Use Prismaselectto limit returned fieldsPer repo guidelines, add
selectto reads/updates to avoid returning full rows you don’t use.Example diffs:
@@ - const foundToken = await prisma.verificationToken.findFirst({ - where: { - token, - }, - }); + const foundToken = await prisma.verificationToken.findFirst({ + where: { token }, + select: { id: true, expires: true, secondaryEmailId: true, identifier: true }, + }); @@ - const user = await prisma.user.findFirst({ - where: { - email: foundToken?.identifier, - }, - }); + const user = await prisma.user.findFirst({ + where: { email: foundToken?.identifier }, + select: { id: true, email: true, emailVerified: true, metadata: true, completedOnboarding: true }, + }); @@ - await prisma.secondaryEmail.update({ + await prisma.secondaryEmail.update({ where: { id: foundToken.secondaryEmailId, email: foundToken?.identifier, }, - data: { - emailVerified: new Date(), - }, + data: { emailVerified: new Date() }, + select: { id: true }, }); @@ - await prisma.user.update({ + await prisma.user.update({ where: { id: user.id }, data: { email: updatedEmail, metadata: userMetadataParsed, }, + select: { id: true }, }); @@ - await prisma.secondaryEmail.update({ + await prisma.secondaryEmail.update({ where: { id: existingSecondaryUser.id, userId: user.id, }, data: { email: oldEmail, emailVerified: user.emailVerified, }, + select: { id: true }, }); @@ - await prisma.user.update({ + await prisma.user.update({ where: { id: user.id }, - data: { - emailVerified: new Date(), - }, + data: { emailVerified: new Date() }, + select: { id: true }, });Also applies to: 79-84, 93-98, 104-112, 123-131, 143-152, 162-169
packages/trpc/server/routers/viewer/attributes/create.handler.ts (1)
28-32: Fix user-facing grammar in error messages“apart of” → “a part of”.
- message: "You need to be apart of an organization to use this feature", + message: "You need to be a part of an organization to use this feature", @@ - message: "You need to be apart of this organization to use this feature", + message: "You need to be a part of this organization to use this feature",Also applies to: 44-49
🧹 Nitpick comments (35)
packages/trpc/server/routers/viewer/me/checkForInvalidAppCredentials.ts (2)
27-31: Filter out potential null teamIds to avoidIN (NULL)and wasted OR branch.
If any memberships haveteamId = null, the currentin: [...]may be noisy. Filter before use.Apply:
- + const teamIds = userTeamIds.map((m) => m.teamId).filter((id): id is string => Boolean(id)); const apps = await prisma.credential.findMany({ where: { - OR: [{ userId }, { teamId: { in: userTeamIds.map((membership) => membership.teamId) } }], + OR: [{ userId }, { teamId: { in: teamIds } }], invalid: true, }, select: { appId: true, }, });
38-46: Parallelize app metadata fetches and de-duplicate appIds.
Avoid N serial awaits and duplicate banners if multiple invalid creds exist for the same app.- const appNamesAndSlugs: InvalidAppCredentialBannerProps[] = []; - for (const app of apps) { - if (app.appId) { - const appId = app.appId; - const appMeta = await getAppFromSlug(appId); - const name = appMeta ? appMeta.name : appId; - appNamesAndSlugs.push({ slug: appId, name }); - } - } + const uniqueAppIds = Array.from( + new Set(apps.map((a) => a.appId).filter((id): id is string => Boolean(id))) + ); + const appNamesAndSlugs: InvalidAppCredentialBannerProps[] = await Promise.all( + uniqueAppIds.map(async (appId) => { + const appMeta = await getAppFromSlug(appId); + return { slug: appId, name: appMeta?.name ?? appId }; + }) + );packages/app-store/routing-forms/trpc/utils.ts (3)
166-184: Minor: computescheduledAtonce per batch
scheduledAtis identical for all items; compute once to avoid redundant Day.js work in the loop.- const promisesFormSubmittedNoEvent = webhooksFormSubmittedNoEvent.map((webhook) => { - const scheduledAt = dayjs().add(15, "minute").toDate(); - - return tasker.create( + const scheduledAt = dayjs().add(15, "minute").toDate(); + const promisesFormSubmittedNoEvent = webhooksFormSubmittedNoEvent.map((webhook) => + tasker.create( "triggerFormSubmittedNoEventWebhook", { responseId, form: { id: form.id, name: form.name, teamId: form.teamId ?? null, }, responses: fieldResponsesByIdentifier, redirect: chosenAction, webhook, }, { scheduledAt } - ); - }); + ) + );
142-142: Consistency: use enum in payload tooUse
WebhookTriggerEvents.FORM_SUBMITTEDinstead of the string literal to keep event values centralized. Covered in the diff above.
233-237: Nit: boolean naming vs. value type
isTeamFormholdsnumber | null, not a boolean. Either coerce to boolean or rename.- const isTeamForm = form.teamId; + const isTeamForm = form.teamId != null;packages/app-store/routing-forms/trpc/onFormSubmission.test.ts (1)
67-81: Add a test to ensure webhooks are awaitedGiven the async fix in
utils.ts, add a test that fails if webhook promises aren’t returned/awaited.describe("Webhooks", () => { + it("awaits FORM_SUBMITTED webhook promises", async () => { + const deferred: { resolve: () => void } = { resolve: () => {} }; + vi.mocked(getWebhooks).mockResolvedValueOnce([{ id: "wh-1", secret: "secret" } as any]); + vi.mocked(sendGenericWebhookPayload).mockImplementation( + () => + new Promise((res) => { + deferred.resolve = res as () => void; + }) + ); + + const p = _onFormSubmission(mockForm as any, mockResponse, responseId); + // At this point, promise should still be pending + expect(vi.mocked(sendGenericWebhookPayload)).toHaveBeenCalledTimes(1); + // Resolve the deferred webhook send + deferred.resolve(); + await expect(p).resolves.toBeUndefined(); + });apps/web/server/lib/[user]/[type]/getServerSideProps.ts (1)
153-154: Fix typos in comment.
Minor readability nit.-// as well as to check if the event exist, so we c an show a 404 otherwise. +// as well as to check if the event exists, so we can show a 404 otherwise.apps/web/playwright/organization/organization-invitation.e2e.ts (3)
548-549: Replace arbitrary timeout with a deterministic wait.
Hard waits are flaky; prefer networkidle or UI readiness.- await page.waitForLoadState("domcontentloaded"); - await page.waitForTimeout(500); // Add a small delay to ensure UI is fully loaded + await page.waitForLoadState("networkidle");
608-609: Ditto: avoid fixed sleep in team member check.- await page.waitForLoadState("domcontentloaded"); - await page.waitForTimeout(1000); // Add a small delay to ensure UI is fully loaded + await page.waitForLoadState("networkidle");
500-503: Close browser context to free resources.
Prevents leakage across tests.await inviteLinkPage.locator("button[type=submit]").click(); await inviteLinkPage.waitForURL("/getting-started"); + await context.close(); return { email };apps/web/modules/settings/admin/components/UsersTable.tsx (2)
61-71: Localize action labels and alt text with t().Per frontend guidelines, avoid hardcoded strings; use t() for actions and alt text.
const actions = [ { id: "unlock-sms", - label: smsLockState === SMSLockState.LOCKED ? "Unlock SMS sending" : "Lock SMS sending", + label: + smsLockState === SMSLockState.LOCKED + ? t("unlock_sms_sending") + : t("lock_sms_sending"), onClick: () => setSMSLockState({ userId: user ? user.id : undefined, teamId: team ? team.id : undefined, lock: smsLockState !== SMSLockState.LOCKED, }), icon: "lock" as IconName, }, ]; if (smsLockState === SMSLockState.REVIEW_NEEDED) { actions.push({ id: "reviewed", - label: "Mark as Reviewed", + label: t("mark_as_reviewed"), onClick: () => setSMSLockState({ userId: user ? user.id : undefined, teamId: team ? team.id : undefined, lock: false, }), icon: "pencil" as IconName, }); } @@ - alt={`Avatar of ${user.username || "Nameless"}`} + alt={t("avatar_of_name", { name: user.username || t("nameless") })} @@ - alt={`Avatar of ${team.name}`} + alt={t("avatar_of_name", { name: team.name })}Also applies to: 73-85, 109-109, 133-133
153-153: Prefer named exports over default exports.Improves tree-shaking and refactors.
-export default UsersTable; +export { UsersTable };apps/web/playwright/webhook.e2e.ts (1)
371-371: Remove redundant non-null assertion after await.The
!here is a no-op on Promise and can mislead readers.- const newBooking = await prisma.booking.findFirst({ where: { fromReschedule: booking?.uid } })!; + const newBooking = await prisma.booking.findFirst({ where: { fromReschedule: booking?.uid } });packages/trpc/server/routers/viewer/admin/setSMSLockState.handler.ts (1)
16-16: Prefer named exports over default exports (tree-shaking, refactors).Convert to a named export.
-const setSMSLockState = async ({ input }: GetOptions) => { +export const setSMSLockState = async ({ input }: GetOptions) => { ... -}; - -export default setSMSLockState; +};Also applies to: 88-88
apps/web/playwright/dynamic-booking-pages.e2e.ts (1)
85-86: Remove unnecessaryawaiton Locator creation.
page.getByTestId(...)returns a Locator synchronously; awaiting it is redundant.- let listItemLocator = await page.getByTestId(listItemByDurationTestId(duration)); + let listItemLocator = page.getByTestId(listItemByDurationTestId(duration)); ... - listItemLocator = await page.getByTestId(listItemByDurationTestId(duration)); + listItemLocator = page.getByTestId(listItemByDurationTestId(duration)); ... - listItemLocator = await page.getByTestId(listItemByDurationTestId(15)); + listItemLocator = page.getByTestId(listItemByDurationTestId(15));Also applies to: 92-93, 98-99
packages/lib/server/repository/membership.ts (2)
479-491: Use enum values instead of string literals in role filter.Prevents drift and benefits from type-safety.
- role: { - in: ["ADMIN", "OWNER"], - }, + role: { + in: [MembershipRole.ADMIN, MembershipRole.OWNER], + },
166-221: Broaden migration fromincludetoselectin this repository.Multiple queries still rely on
include. For consistency with our guideline and to reduce over-fetching, consider phasing them toselect(pattern shown above). I can help generate targeted diffs per query if desired.Also applies to: 292-303, 343-350, 429-445
apps/web/playwright/reschedule.e2e.ts (1)
6-7: Merge duplicate imports from the same moduleMinor tidy-up: combine adjacent imports from
@calcom/prisma/enums.-import { MembershipRole } from "@calcom/prisma/enums"; -import { BookingStatus } from "@calcom/prisma/enums"; +import { BookingStatus, MembershipRole } from "@calcom/prisma/enums";packages/features/ee/teams/components/RoundRobinSettings.tsx (1)
43-47: Fix typo in commentSmall spelling/grammar tweak in the inline comment.
- // Rounb robin reset interval / basis governs host selection logic on the booking page. + // Round robin reset interval/basis govern host selection logic on the booking page.apps/web/playwright/bookings-list.e2e.ts (1)
431-440: Preferselectoverincludein Prisma queries (tests too)Follow repo guideline to select only needed fields; avoids unnecessary payload and keeps tests fast.
- const host = await prisma.membership.findFirstOrThrow({ + const host = await prisma.membership.findFirstOrThrow({ where: { teamId: team.id, userId: { not: owner.id, }, }, - include: { - user: true, - }, + select: { + user: { + select: { + id: true, + username: true, + name: true, + }, + }, + }, });apps/web/lib/team/[slug]/getServerSideProps.tsx (1)
118-138: Optional: preferselectoverincludefor Prisma fetchesNot critical here (TSX file), but to stay consistent with our “select-only” guidance, consider switching to
selectand enumerating needed fields.Example shape:
- const unpublishedTeam = await prisma.team.findFirst({ - where: { /* ... */ }, - include: { - parent: { - select: { id: true, slug: true, name: true, isPrivate: true, isOrganization: true, metadata: true, logoUrl: true }, - }, - }, - }); + const unpublishedTeam = await prisma.team.findFirst({ + where: { /* ... */ }, + select: { + /* list fields from team you actually use ... */ + parent: { + select: { id: true, slug: true, name: true, isPrivate: true, isOrganization: true, metadata: true, logoUrl: true }, + }, + }, + });packages/trpc/server/routers/viewer/attributes/delete.handler.ts (1)
21-26: Fix typo in user-facing error messages (“apart” → “a part”)Small copy fix in API errors.
- message: "You need to be apart of an organization to use this feature", + message: "You need to be a part of an organization to use this feature",- message: "You need to be apart of this organization to use this feature", + message: "You need to be a part of this organization to use this feature",Also applies to: 38-43
packages/app-store/routing-forms/pages/route-builder/[...appPages].tsx (2)
111-114: Return a boolean fromhasRulesCurrent code returns a number or
undefined. Make it explicit.-const hasRules = (route: EditFormRoute) => { - if (isRouter(route)) return false; - route.queryValue.children1 && Object.keys(route.queryValue.children1).length; -}; +const hasRules = (route: EditFormRoute) => { + if (isRouter(route)) return false; + return !!(route.queryValue.children1 && Object.keys(route.queryValue.children1).length); +};
533-533: Localize user-facing strings (uset())Per guidelines for TSX, wrap visible strings with
t(). Below are examples; adjust keys to your i18n catalog.- <span className="text-emphasis ml-2 text-sm font-medium">Conditions</span> + <span className="text-emphasis ml-2 text-sm font-medium">{t("conditions")}</span>- <span className="text-emphasis ml-2 text-sm font-medium"> - And connect with specific team members - </span> + <span className="text-emphasis ml-2 text-sm font-medium"> + {t("and_connect_with_specific_team_members")} + </span>- <span className="text-emphasis ml-2 text-sm font-medium">Fallback</span> + <span className="text-emphasis ml-2 text-sm font-medium">{t("fallback")}</span>- label={route.name ?? (route.isFallback ? "Otherwise" : `Route ${index + 1}`)} + label={route.name ?? (route.isFallback ? t("otherwise") : t("route_n", { n: index + 1 }))}- <p className="text-subtle mt-2 text-sm"> - Fields available in <span className="font-bold">{route.name}</span> will be added to this form. - </p> + <p className="text-subtle mt-2 text-sm"> + {t("fields_available_will_be_added_to_form", { routeName: route.name })} + </p>- if (action.type === "customPageMessage") { - action.value = "We are not ready for you yet :("; + if (action.type === "customPageMessage") { + action.value = t("not_ready_yet_message");- placeholder="https://example.com" + placeholder={t("example_url_placeholder")}- placeholder="event-url" + placeholder={t("event_url_placeholder")}- <EmptyState - icon="menu" - header="Create your first route" - text="Routes determine where your form responses will be sent based on the answers provided." + <EmptyState + icon="menu" + header={t("create_your_first_route")} + text={t("routes_determine_destination_helptext")} buttonText={t("add_a_new_route")}Also applies to: 583-585, 611-612, 639-641, 498-500, 726-756, 860-892, 761-776, 897-925, 1326-1343
packages/trpc/server/routers/viewer/attributes/toggleActive.handler.ts (2)
57-61: Consider using “FORBIDDEN” for authorization failures.User is authenticated but lacks permission; “FORBIDDEN” may be semantically clearer than “UNAUTHORIZED”. Verify client expectations before changing.
- throw new TRPCError({ - code: "UNAUTHORIZED", + throw new TRPCError({ + code: "FORBIDDEN", message: "You don't have permission to modify attributes", });
100-100: Prefer named exports over default exports.Improves tree-shaking and refactoring.
-export default toggleActiveHandler; +export { toggleActiveHandler };packages/trpc/server/routers/viewer/attributes/edit.handler.ts (3)
59-63: Consider “FORBIDDEN” for permission denials.Semantically clearer if the user is authenticated but not permitted. Confirm consumer handling first.
- throw new TRPCError({ - code: "UNAUTHORIZED", + throw new TRPCError({ + code: "FORBIDDEN", message: "You don't have permission to edit attributes", });
182-184: Consider “FORBIDDEN” here as well for ownership checks.This is an authorization failure rather than authentication.
- throw new TRPCError({ - code: "UNAUTHORIZED", + throw new TRPCError({ + code: "FORBIDDEN", message: "You can't edit options that are not owned by the attribute", });
188-189: Prefer named exports over default exports.-export default editAttributesHandler; +export { editAttributesHandler };apps/web/playwright/booking-pages.e2e.ts (2)
719-735: Use Prismaselectinstead ofincludein tests, too.Even in tests, prefer
selectper repo guidelines and to minimize payload.- const eventWithPrivateLink = await prisma.eventType.update({ + const eventWithPrivateLink = await prisma.eventType.update({ where: { id: eventType.id, }, data: { hashedLink: { create: [ { link: generateHashedLink(eventType.id), }, ], }, }, - include: { - hashedLink: true, - }, + select: { + slug: true, + hashedLink: { + select: { link: true } + } + }, });
569-572: Remove duplicate assertion.The success-page assertion is executed twice consecutively; keep one.
- await expect(page.locator("[data-testid=success-page]")).toBeVisible(); - await expect(page.locator("[data-testid=success-page]")).toBeVisible();apps/web/lib/pages/auth/verify-email.ts (1)
175-175: Avoid possible double slash in redirect URLRemove the extra “/” before the path.
-return res.redirect(`${WEBAPP_URL}/${hasCompletedOnboarding ? "/event-types" : "/getting-started"}`); +return res.redirect(`${WEBAPP_URL}${hasCompletedOnboarding ? "/event-types" : "/getting-started"}`);packages/trpc/server/routers/viewer/attributes/create.handler.ts (3)
22-23: Avoid stringly-typed attribute typesPrefer enum constants over string literals to prevent drift.
-const typesWithOptions = ["SINGLE_SELECT", "MULTI_SELECT"]; +import { AttributeType } from "@calcom/prisma/enums"; +const typesWithOptions: ReadonlyArray<AttributeType> = [ + AttributeType.SINGLE_SELECT, + AttributeType.MULTI_SELECT, +];
87-91: Return a typed tRPC error on unique constraint violationsUse
TRPCError({ code: "CONFLICT" })instead of a genericError.- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") { - throw new Error("A record with that name already exists. Please choose another one."); - } else { + if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") { + throw new TRPCError({ code: "CONFLICT", message: "A record with that name already exists. Please choose another one." }); + } else { throw error; // Re-throw the error if it's not a unique constraint violation }
110-110: Prefer named export over default exportImproves tree-shaking and refactorability.
-export default createAttributesHandler; +export { createAttributesHandler };
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (43)
apps/api/v1/pages/api/event-types/_post.ts(1 hunks)apps/web/app/(use-page-wrapper)/auth/error/page.tsx(1 hunks)apps/web/app/api/auth/two-factor/totp/disable/route.ts(1 hunks)apps/web/components/apps/installation/EventTypeConferencingAppSettings.tsx(1 hunks)apps/web/lib/handleOrgRedirect.test.ts(1 hunks)apps/web/lib/pages/auth/verify-email.test.ts(1 hunks)apps/web/lib/pages/auth/verify-email.ts(1 hunks)apps/web/lib/reschedule/[uid]/getServerSideProps.ts(1 hunks)apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx(1 hunks)apps/web/lib/team/[slug]/getServerSideProps.tsx(1 hunks)apps/web/modules/settings/admin/components/UsersTable.tsx(1 hunks)apps/web/playwright/booking-limits.e2e.ts(1 hunks)apps/web/playwright/booking-pages.e2e.ts(1 hunks)apps/web/playwright/bookings-list.e2e.ts(1 hunks)apps/web/playwright/dynamic-booking-pages.e2e.ts(1 hunks)apps/web/playwright/embed-code-generator.e2e.ts(1 hunks)apps/web/playwright/lib/orgMigration.ts(1 hunks)apps/web/playwright/organization/organization-invitation.e2e.ts(1 hunks)apps/web/playwright/reschedule.e2e.ts(1 hunks)apps/web/playwright/webhook.e2e.ts(1 hunks)apps/web/server/lib/[user]/[type]/getServerSideProps.ts(1 hunks)apps/web/server/lib/[user]/getServerSideProps.ts(1 hunks)apps/web/test/utils/bookingScenario/getSampleUserInSession.ts(1 hunks)packages/app-store/routing-forms/pages/route-builder/[...appPages].tsx(1 hunks)packages/app-store/routing-forms/trpc/onFormSubmission.test.ts(1 hunks)packages/app-store/routing-forms/trpc/utils.ts(1 hunks)packages/features/bookings/lib/handleNewRecurringBooking.ts(1 hunks)packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts(1 hunks)packages/features/ee/organizations/pages/organization.tsx(1 hunks)packages/features/ee/teams/components/RoundRobinSettings.tsx(1 hunks)packages/lib/bookings/filterHostsByLeadThreshold.test.ts(1 hunks)packages/lib/server/repository/membership.ts(1 hunks)packages/lib/server/repository/workflow.ts(1 hunks)packages/lib/service/attribute/utils.ts(1 hunks)packages/trpc/server/routers/viewer/admin/getSMSLockStateTeamsUsers.handler.ts(1 hunks)packages/trpc/server/routers/viewer/admin/setSMSLockState.handler.ts(1 hunks)packages/trpc/server/routers/viewer/attributes/create.handler.ts(1 hunks)packages/trpc/server/routers/viewer/attributes/delete.handler.ts(1 hunks)packages/trpc/server/routers/viewer/attributes/edit.handler.ts(1 hunks)packages/trpc/server/routers/viewer/attributes/toggleActive.handler.ts(1 hunks)packages/trpc/server/routers/viewer/eventTypes/update.handler.ts(1 hunks)packages/trpc/server/routers/viewer/me/checkForInvalidAppCredentials.ts(1 hunks)packages/trpc/server/routers/viewer/slots/util.ts(1 hunks)
✅ Files skipped from review due to trivial changes (6)
- packages/lib/bookings/filterHostsByLeadThreshold.test.ts
- packages/trpc/server/routers/viewer/eventTypes/update.handler.ts
- packages/features/bookings/lib/handleNewRecurringBooking.ts
- apps/api/v1/pages/api/event-types/_post.ts
- apps/web/lib/pages/auth/verify-email.test.ts
- apps/web/playwright/lib/orgMigration.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/trpc/server/routers/viewer/attributes/edit.handler.tspackages/trpc/server/routers/viewer/attributes/toggleActive.handler.tsapps/web/playwright/embed-code-generator.e2e.tsapps/web/playwright/webhook.e2e.tspackages/trpc/server/routers/viewer/admin/setSMSLockState.handler.tspackages/lib/service/attribute/utils.tspackages/app-store/routing-forms/trpc/utils.tspackages/trpc/server/routers/viewer/admin/getSMSLockStateTeamsUsers.handler.tsapps/web/server/lib/[user]/getServerSideProps.tsapps/web/app/api/auth/two-factor/totp/disable/route.tspackages/trpc/server/routers/viewer/me/checkForInvalidAppCredentials.tspackages/lib/server/repository/workflow.tspackages/trpc/server/routers/viewer/slots/util.tspackages/lib/server/repository/membership.tsapps/web/playwright/booking-limits.e2e.tspackages/features/ee/billing/api/webhook/_customer.subscription.updated.tspackages/app-store/routing-forms/trpc/onFormSubmission.test.tspackages/trpc/server/routers/viewer/attributes/create.handler.tsapps/web/playwright/organization/organization-invitation.e2e.tsapps/web/test/utils/bookingScenario/getSampleUserInSession.tsapps/web/lib/handleOrgRedirect.test.tsapps/web/server/lib/[user]/[type]/getServerSideProps.tsapps/web/playwright/dynamic-booking-pages.e2e.tsapps/web/playwright/reschedule.e2e.tspackages/trpc/server/routers/viewer/attributes/delete.handler.tsapps/web/lib/pages/auth/verify-email.tsapps/web/playwright/bookings-list.e2e.tsapps/web/playwright/booking-pages.e2e.tsapps/web/lib/reschedule/[uid]/getServerSideProps.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
packages/trpc/server/routers/viewer/attributes/edit.handler.tspackages/trpc/server/routers/viewer/attributes/toggleActive.handler.tsapps/web/playwright/embed-code-generator.e2e.tsapps/web/playwright/webhook.e2e.tsapps/web/modules/settings/admin/components/UsersTable.tsxapps/web/components/apps/installation/EventTypeConferencingAppSettings.tsxpackages/trpc/server/routers/viewer/admin/setSMSLockState.handler.tspackages/lib/service/attribute/utils.tsapps/web/lib/team/[slug]/getServerSideProps.tsxpackages/features/ee/organizations/pages/organization.tsxpackages/app-store/routing-forms/trpc/utils.tspackages/trpc/server/routers/viewer/admin/getSMSLockStateTeamsUsers.handler.tsapps/web/server/lib/[user]/getServerSideProps.tsapps/web/app/api/auth/two-factor/totp/disable/route.tspackages/trpc/server/routers/viewer/me/checkForInvalidAppCredentials.tspackages/lib/server/repository/workflow.tspackages/trpc/server/routers/viewer/slots/util.tsapps/web/app/(use-page-wrapper)/auth/error/page.tsxpackages/lib/server/repository/membership.tsapps/web/playwright/booking-limits.e2e.tspackages/features/ee/teams/components/RoundRobinSettings.tsxpackages/features/ee/billing/api/webhook/_customer.subscription.updated.tspackages/app-store/routing-forms/trpc/onFormSubmission.test.tspackages/trpc/server/routers/viewer/attributes/create.handler.tsapps/web/playwright/organization/organization-invitation.e2e.tsapps/web/test/utils/bookingScenario/getSampleUserInSession.tsapps/web/lib/handleOrgRedirect.test.tspackages/app-store/routing-forms/pages/route-builder/[...appPages].tsxapps/web/server/lib/[user]/[type]/getServerSideProps.tsapps/web/playwright/dynamic-booking-pages.e2e.tsapps/web/playwright/reschedule.e2e.tsapps/web/lib/team/[slug]/[type]/getServerSideProps.tsxpackages/trpc/server/routers/viewer/attributes/delete.handler.tsapps/web/lib/pages/auth/verify-email.tsapps/web/playwright/bookings-list.e2e.tsapps/web/playwright/booking-pages.e2e.tsapps/web/lib/reschedule/[uid]/getServerSideProps.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
packages/trpc/server/routers/viewer/attributes/edit.handler.tspackages/trpc/server/routers/viewer/attributes/toggleActive.handler.tsapps/web/playwright/embed-code-generator.e2e.tsapps/web/playwright/webhook.e2e.tsapps/web/modules/settings/admin/components/UsersTable.tsxapps/web/components/apps/installation/EventTypeConferencingAppSettings.tsxpackages/trpc/server/routers/viewer/admin/setSMSLockState.handler.tspackages/lib/service/attribute/utils.tsapps/web/lib/team/[slug]/getServerSideProps.tsxpackages/features/ee/organizations/pages/organization.tsxpackages/app-store/routing-forms/trpc/utils.tspackages/trpc/server/routers/viewer/admin/getSMSLockStateTeamsUsers.handler.tsapps/web/server/lib/[user]/getServerSideProps.tsapps/web/app/api/auth/two-factor/totp/disable/route.tspackages/trpc/server/routers/viewer/me/checkForInvalidAppCredentials.tspackages/lib/server/repository/workflow.tspackages/trpc/server/routers/viewer/slots/util.tsapps/web/app/(use-page-wrapper)/auth/error/page.tsxpackages/lib/server/repository/membership.tsapps/web/playwright/booking-limits.e2e.tspackages/features/ee/teams/components/RoundRobinSettings.tsxpackages/features/ee/billing/api/webhook/_customer.subscription.updated.tspackages/app-store/routing-forms/trpc/onFormSubmission.test.tspackages/trpc/server/routers/viewer/attributes/create.handler.tsapps/web/playwright/organization/organization-invitation.e2e.tsapps/web/test/utils/bookingScenario/getSampleUserInSession.tsapps/web/lib/handleOrgRedirect.test.tspackages/app-store/routing-forms/pages/route-builder/[...appPages].tsxapps/web/server/lib/[user]/[type]/getServerSideProps.tsapps/web/playwright/dynamic-booking-pages.e2e.tsapps/web/playwright/reschedule.e2e.tsapps/web/lib/team/[slug]/[type]/getServerSideProps.tsxpackages/trpc/server/routers/viewer/attributes/delete.handler.tsapps/web/lib/pages/auth/verify-email.tsapps/web/playwright/bookings-list.e2e.tsapps/web/playwright/booking-pages.e2e.tsapps/web/lib/reschedule/[uid]/getServerSideProps.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Always use
t()for text localization in frontend code; direct text embedding should trigger a warning
Files:
apps/web/modules/settings/admin/components/UsersTable.tsxapps/web/components/apps/installation/EventTypeConferencingAppSettings.tsxapps/web/lib/team/[slug]/getServerSideProps.tsxpackages/features/ee/organizations/pages/organization.tsxapps/web/app/(use-page-wrapper)/auth/error/page.tsxpackages/features/ee/teams/components/RoundRobinSettings.tsxpackages/app-store/routing-forms/pages/route-builder/[...appPages].tsxapps/web/lib/team/[slug]/[type]/getServerSideProps.tsx
🧠 Learnings (8)
📚 Learning: 2025-08-17T22:00:16.329Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts:117-126
Timestamp: 2025-08-17T22:00:16.329Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts, the enabled input parameter in the update endpoint is intentionally not forwarded to aiService.updateAgentConfiguration() as the enabled/disabled agent functionality is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.
Applied to files:
packages/trpc/server/routers/viewer/attributes/toggleActive.handler.ts
📚 Learning: 2025-08-27T13:32:46.887Z
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
Applied to files:
apps/web/playwright/webhook.e2e.tsapps/web/playwright/booking-limits.e2e.tspackages/app-store/routing-forms/trpc/onFormSubmission.test.tsapps/web/lib/handleOrgRedirect.test.tsapps/web/playwright/dynamic-booking-pages.e2e.tsapps/web/playwright/bookings-list.e2e.tsapps/web/playwright/booking-pages.e2e.ts
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
Applied to files:
packages/trpc/server/routers/viewer/slots/util.tsapps/web/playwright/booking-pages.e2e.tsapps/web/lib/reschedule/[uid]/getServerSideProps.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.
Applied to files:
packages/lib/server/repository/membership.tsapps/web/lib/reschedule/[uid]/getServerSideProps.ts
📚 Learning: 2025-08-21T16:34:10.839Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/phoneNumber/delete.handler.ts:13-24
Timestamp: 2025-08-21T16:34:10.839Z
Learning: In calcom/cal.com PR #22995, the deletePhoneNumber function in packages/trpc/server/routers/viewer/phoneNumber/delete.handler.ts is only used for imported phone numbers that don't have active Stripe subscriptions. Purchased phone numbers with subscriptions use a separate cancellation flow first (per maintainer Udit-takkar).
Applied to files:
packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts
📚 Learning: 2025-08-20T17:34:35.004Z
Learnt from: mdm317
PR: calcom/cal.com#23221
File: packages/features/schedules/components/NewScheduleButton.tsx:1-1
Timestamp: 2025-08-20T17:34:35.004Z
Learning: In the Cal.com codebase, server actions like `revalidateAvailabilityList()` from `app/(use-page-wrapper)/(main-nav)/availability/actions` are imported and called directly in client components within mutation `onSuccess` callbacks. This pattern is consistently used across availability-related components (both in availability-view.tsx for updates and NewScheduleButton.tsx for creates) to ensure the availability list cache is fresh after mutations.
Applied to files:
apps/web/lib/reschedule/[uid]/getServerSideProps.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.
Applied to files:
apps/web/lib/reschedule/[uid]/getServerSideProps.ts
📚 Learning: 2025-08-19T09:47:49.478Z
Learnt from: eunjae-lee
PR: calcom/cal.com#23166
File: packages/prisma/migrations/20250818151914_routing_form_response_denormalized_backfill2/migration.sql:65-66
Timestamp: 2025-08-19T09:47:49.478Z
Learning: The Booking table has a unique constraint and index on the uid column (defined as `uid String unique` in schema.prisma), so JOINs on Booking.uid have optimal performance and don't require additional indexing.
Applied to files:
apps/web/lib/reschedule/[uid]/getServerSideProps.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Production builds / Build Docs
- GitHub Check: Production builds / Build Web App
- GitHub Check: Production builds / Build API v2
- GitHub Check: Production builds / Build Atoms
- GitHub Check: Production builds / Build API v1
- GitHub Check: Tests / Unit
- GitHub Check: Linters / lint
- GitHub Check: Type check / check-types
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/api/v2/src/modules/workflows/workflows.repository.ts (3)
23-33: Replace include with select per Prisma query guidelinesRepository methods should avoid
includeand select only required fields. This reduces payloads and risk of leaking fields not needed downstream.Would you like a follow-up PR to convert this to
selectwith an explicit shape aligned to the API response?
42-45: Also switch this include to selectSame rationale as above: minimize fetched shape for
stepsandactiveOn.
18-20: Add a valid unique selector or switch to deleteManyThe Prisma call in apps/api/v2/src/modules/workflows/workflows.repository.ts (lines 18–20) uses
delete({ where: { id: workflowId, teamId } })but the
Workflowmodel in your schema only hasidas a unique key—there’s no@@unique([id, teamId]). This will fail at compile time.• Add a compound unique on
(id, teamId)inschema.prismaand update to:where: { id_teamId: { id: workflowId, teamId } }(adjusting the constraint name as needed).
• Or removeteamIdfrom thedeleteselector (leavingwhere: { id: workflowId }) and enforce team ownership in code.
• Alternatively, usedeleteMany({ where: { id: workflowId, teamId } })if you intend to delete by both fields without a unique index.
🧹 Nitpick comments (3)
apps/api/v2/src/modules/workflows/workflows.repository.ts (3)
9-10: Prefer model types from @prisma/client (type-only) to avoid cross-package type driftEnums from
@calcom/prisma/enumsare correct. For model types, using@prisma/clientas a type-only import avoids accidental divergence between multiple Prisma client packages.-import type { Workflow, WorkflowStep } from "@calcom/prisma/client"; +import type { Workflow, WorkflowStep } from "@prisma/client";
37-48: Add stable ordering for pagination determinism
skip/takewithoutorderBycan yield non-deterministic pages across queries. Add a stable sort (e.g., byid).const workflows = await this.dbRead.prisma.workflow.findMany({ where: { teamId: teamId, }, - include: { + include: { steps: true, activeOn: { select: { eventTypeId: true } }, }, + orderBy: { id: "desc" }, skip, take, });
72-76: Avoid double cast (unknown as PrismaClient); unify types or narrow required interfaceThe
as unknown as PrismaClientcast is unsound. Prefer aligning the Prisma client type across packages or changeupdateWorkflowto accept a minimal interface (only the models it uses), avoiding cross-package nominal-type conflicts.Happy to sketch the minimal
PrismaLikeinterface and a small adapter if useful.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/api/v2/src/modules/workflows/workflows.repository.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Avoid dot-suffixes like
.service.tsor.repository.tsfor new files; reserve.test.ts,.spec.ts,.types.tsfor their specific purposes
Files:
apps/api/v2/src/modules/workflows/workflows.repository.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
apps/api/v2/src/modules/workflows/workflows.repository.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
apps/api/v2/src/modules/workflows/workflows.repository.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
apps/api/v2/src/modules/workflows/workflows.repository.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Production builds / Build API v2
- GitHub Check: Production builds / Build Atoms
- GitHub Check: Tests / Unit
- GitHub Check: Production builds / Build API v1
- GitHub Check: Production builds / Build Web App
- GitHub Check: Type check / check-types
- GitHub Check: Linters / lint
🔇 Additional comments (1)
apps/api/v2/src/modules/workflows/workflows.repository.ts (1)
57-60: Enums migration LGTMUsing
WorkflowTriggerEvents.BEFORE_EVENTandTimeUnit.HOURfrom@calcom/prisma/enumsmatches the PR goal and avoids pulling the full Prisma client at runtime.
E2E results are ready! |
What does this PR do?
@calcom/prisma/enumsinstead of@calcom/prisma/clientor@prisma/client.Impact
With
@prisma/client: Bundles ~28MB of prisma client codeWith
@calcom/prisma/enums: Bundles ~0.1KBMandatory Tasks (DO NOT REMOVE)
How should this be tested?