diff --git a/apps/api/v1/pages/api/destination-calendars/[id]/_patch.ts b/apps/api/v1/pages/api/destination-calendars/[id]/_patch.ts index c23d8b151b..63b63f48cd 100644 --- a/apps/api/v1/pages/api/destination-calendars/[id]/_patch.ts +++ b/apps/api/v1/pages/api/destination-calendars/[id]/_patch.ts @@ -2,7 +2,7 @@ import type { Prisma } from "@prisma/client"; import type { NextApiRequest } from "next"; import type { z } from "zod"; -import { getCalendarCredentialsWithoutDwd, getConnectedCalendars } from "@calcom/lib/CalendarManager"; +import { getCalendarCredentialsWithoutDelegation, getConnectedCalendars } from "@calcom/lib/CalendarManager"; import { HttpError } from "@calcom/lib/http-error"; import { defaultResponder } from "@calcom/lib/server/defaultResponder"; import prisma from "@calcom/prisma"; @@ -185,7 +185,7 @@ async function verifyCredentialsAndGetId({ currentCredentialId: number | null; }) { if (parsedBody.integration && parsedBody.externalId) { - const calendarCredentials = getCalendarCredentialsWithoutDwd(userCredentials); + const calendarCredentials = getCalendarCredentialsWithoutDelegation(userCredentials); const { connectedCalendars } = await getConnectedCalendars( calendarCredentials, diff --git a/apps/api/v1/pages/api/destination-calendars/_post.ts b/apps/api/v1/pages/api/destination-calendars/_post.ts index 06a133c4bc..2962224a7f 100644 --- a/apps/api/v1/pages/api/destination-calendars/_post.ts +++ b/apps/api/v1/pages/api/destination-calendars/_post.ts @@ -1,6 +1,6 @@ import type { NextApiRequest } from "next"; -import { getCalendarCredentialsWithoutDwd, getConnectedCalendars } from "@calcom/lib/CalendarManager"; +import { getCalendarCredentialsWithoutDelegation, getConnectedCalendars } from "@calcom/lib/CalendarManager"; import { HttpError } from "@calcom/lib/http-error"; import { defaultResponder } from "@calcom/lib/server/defaultResponder"; import prisma from "@calcom/prisma"; @@ -82,7 +82,7 @@ async function postHandler(req: NextApiRequest) { message: "Bad request, credential id invalid", }); - const calendarCredentials = getCalendarCredentialsWithoutDwd(userCredentials); + const calendarCredentials = getCalendarCredentialsWithoutDelegation(userCredentials); const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, [], parsedBody.externalId); diff --git a/apps/api/v1/test/lib/selected-calendars/_post.test.ts b/apps/api/v1/test/lib/selected-calendars/_post.test.ts index 8975e25489..28932ade52 100644 --- a/apps/api/v1/test/lib/selected-calendars/_post.test.ts +++ b/apps/api/v1/test/lib/selected-calendars/_post.test.ts @@ -81,7 +81,7 @@ describe("POST /api/selected-calendars", () => { userId: 444444, id: "xxx-xxx", eventTypeId: null, - domainWideDelegationCredentialId: null, + delegationCredentialId: null, googleChannelId: null, googleChannelKind: null, googleChannelResourceId: null, @@ -123,7 +123,7 @@ describe("POST /api/selected-calendars", () => { googleChannelResourceId: null, googleChannelResourceUri: null, googleChannelExpiration: null, - domainWideDelegationCredentialId: null, + delegationCredentialId: null, eventTypeId: null, error: null, }); diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index 1a22fe4371..f4956799cc 100644 --- a/apps/api/v2/package.json +++ b/apps/api/v2/package.json @@ -30,7 +30,7 @@ "@axiomhq/winston": "^1.2.0", "@calcom/platform-constants": "*", "@calcom/platform-enums": "*", - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.124", + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.126", "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2", "@calcom/platform-types": "*", "@calcom/platform-utils": "*", diff --git a/apps/api/v2/src/ee/calendars/outputs/connected-calendars.output.ts b/apps/api/v2/src/ee/calendars/outputs/connected-calendars.output.ts index d87cb59a2e..30b291ce9d 100644 --- a/apps/api/v2/src/ee/calendars/outputs/connected-calendars.output.ts +++ b/apps/api/v2/src/ee/calendars/outputs/connected-calendars.output.ts @@ -131,7 +131,7 @@ class Primary { @IsString() @IsOptional() @ApiPropertyOptional({ type: String, nullable: true }) - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; } export class Calendar { @@ -174,7 +174,7 @@ export class Calendar { @IsString() @IsOptional() @ApiPropertyOptional({ type: String, nullable: true }) - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; } export class ConnectedCalendar { @@ -190,7 +190,7 @@ export class ConnectedCalendar { @IsString() @IsOptional() @ApiPropertyOptional({ type: String, nullable: true }) - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; @ValidateNested() @IsObject() @@ -238,7 +238,7 @@ class DestinationCalendar { @IsString() @IsOptional() @ApiPropertyOptional({ type: String, nullable: true }) - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; @IsString() @IsOptional() diff --git a/apps/api/v2/src/ee/calendars/services/calendars.service.ts b/apps/api/v2/src/ee/calendars/services/calendars.service.ts index ef07970547..c338754ef9 100644 --- a/apps/api/v2/src/ee/calendars/services/calendars.service.ts +++ b/apps/api/v2/src/ee/calendars/services/calendars.service.ts @@ -39,7 +39,7 @@ export class CalendarsService { private readonly selectedCalendarsRepository: SelectedCalendarsRepository ) {} - private buildNonDwdCredentials(credentials: TCredential[]) { + private buildNonDelegationCredentials(credentials: TCredential[]) { return credentials .map((credential) => ({ ...credential, @@ -83,7 +83,7 @@ export class CalendarsService { ); try { const calendarBusyTimes = await getBusyCalendarTimes( - this.buildNonDwdCredentials(credentials), + this.buildNonDelegationCredentials(credentials), dateFrom, dateTo, composedSelectedCalendars diff --git a/apps/api/v2/src/modules/atoms/services/event-types-atom.service.ts b/apps/api/v2/src/modules/atoms/services/event-types-atom.service.ts index e38d630bb9..6499500161 100644 --- a/apps/api/v2/src/modules/atoms/services/event-types-atom.service.ts +++ b/apps/api/v2/src/modules/atoms/services/event-types-atom.service.ts @@ -210,7 +210,7 @@ export class EventTypesAtomService { credentials = credentials.concat(teamAppCredentials); } } - //TODO: enrich credentials for DWD + //TODO: enrich credentials for DelegationCredential const enabledApps = await getEnabledAppsFromCredentials(credentials, { where: { slug }, }); diff --git a/apps/api/v2/src/modules/destination-calendars/controllers/destination-calendars.controller.ts b/apps/api/v2/src/modules/destination-calendars/controllers/destination-calendars.controller.ts index 6fad47ba4e..8241f1b867 100644 --- a/apps/api/v2/src/modules/destination-calendars/controllers/destination-calendars.controller.ts +++ b/apps/api/v2/src/modules/destination-calendars/controllers/destination-calendars.controller.ts @@ -29,12 +29,12 @@ export class DestinationCalendarsController { @Body() input: DestinationCalendarsInputBodyDto, @GetUser() user: UserWithProfile ): Promise { - const { integration, externalId, domainWideDelegationCredentialId } = input; + const { integration, externalId, delegationCredentialId } = input; const updatedDestinationCalendar = await this.destinationCalendarsService.updateDestinationCalendars( integration, externalId, user.id, - domainWideDelegationCredentialId + delegationCredentialId ); return { diff --git a/apps/api/v2/src/modules/destination-calendars/destination-calendars.repository.ts b/apps/api/v2/src/modules/destination-calendars/destination-calendars.repository.ts index a95165204c..5d8684f86d 100644 --- a/apps/api/v2/src/modules/destination-calendars/destination-calendars.repository.ts +++ b/apps/api/v2/src/modules/destination-calendars/destination-calendars.repository.ts @@ -12,21 +12,21 @@ export class DestinationCalendarsRepository { userId: number, primaryEmail: string | null, credentialId?: number, - domainWideDelegationCredentialId?: string + delegationCredentialId?: string ) { return await this.dbWrite.prisma.destinationCalendar.upsert({ update: { integration, externalId, ...(credentialId ? { credentialId } : {}), - ...(domainWideDelegationCredentialId ? { domainWideDelegationCredentialId } : {}), + ...(delegationCredentialId ? { delegationCredentialId } : {}), primaryEmail, }, create: { integration, externalId, ...(credentialId ? { credentialId } : {}), - ...(domainWideDelegationCredentialId ? { domainWideDelegationCredentialId } : {}), + ...(delegationCredentialId ? { delegationCredentialId } : {}), primaryEmail, userId, }, diff --git a/apps/api/v2/src/modules/destination-calendars/inputs/destination-calendars.input.ts b/apps/api/v2/src/modules/destination-calendars/inputs/destination-calendars.input.ts index cf6d566eb2..dc584c4ad8 100644 --- a/apps/api/v2/src/modules/destination-calendars/inputs/destination-calendars.input.ts +++ b/apps/api/v2/src/modules/destination-calendars/inputs/destination-calendars.input.ts @@ -37,5 +37,5 @@ export class DestinationCalendarsInputBodyDto { @IsString() @IsOptional() @ApiPropertyOptional() - readonly domainWideDelegationCredentialId?: string; + readonly delegationCredentialId?: string; } diff --git a/apps/api/v2/src/modules/destination-calendars/services/destination-calendars.service.ts b/apps/api/v2/src/modules/destination-calendars/services/destination-calendars.service.ts index a67bde2162..130455b84c 100644 --- a/apps/api/v2/src/modules/destination-calendars/services/destination-calendars.service.ts +++ b/apps/api/v2/src/modules/destination-calendars/services/destination-calendars.service.ts @@ -14,10 +14,10 @@ export class DestinationCalendarsService { integration: string, externalId: string, userId: number, - domainWideDelegationCredentialId?: string + delegationCredentialId?: string ) { - // note(Lauris): todo remove the log but leaving this now to confirm domainWideDelegationCredentialId is received - console.log("debug: domainWideDelegationCredentialId", domainWideDelegationCredentialId); + // note(Lauris): todo remove the log but leaving this now to confirm delegationCredentialId is received + console.log("debug: delegationCredentialId", delegationCredentialId); const userCalendars = await this.calendarsService.getCalendars(userId); const allCalendars: Calendar[] = userCalendars.connectedCalendars .map((cal: ConnectedCalendar) => cal.calendars ?? []) @@ -27,25 +27,25 @@ export class DestinationCalendarsService { cal.externalId === externalId && cal.integration === integration && cal.readOnly === false )?.credentialId; - if (!domainWideDelegationCredentialId && !credentialId) { + if (!delegationCredentialId && !credentialId) { throw new NotFoundException(`Could not find calendar ${externalId}`); } - const dwdCalendar = domainWideDelegationCredentialId + const delegatedCalendar = delegationCredentialId ? allCalendars.find( (cal: Calendar) => cal.externalId === externalId && cal.integration === integration && - cal.domainWideDelegationCredentialId === domainWideDelegationCredentialId + cal.delegationCredentialId === delegationCredentialId ) : undefined; - if (domainWideDelegationCredentialId && !dwdCalendar) { + if (delegationCredentialId && !delegatedCalendar) { throw new NotFoundException(`Could not find calendar ${externalId}`); } - const primaryEmail = dwdCalendar - ? (dwdCalendar.primary && dwdCalendar?.email) || undefined + const primaryEmail = delegatedCalendar + ? (delegatedCalendar.primary && delegatedCalendar?.email) || undefined : allCalendars.find((cal: Calendar) => cal.primary && cal.credentialId === credentialId)?.email; const { @@ -58,8 +58,8 @@ export class DestinationCalendarsService { externalId, userId, primaryEmail ?? null, - dwdCalendar ? undefined : credentialId, - dwdCalendar ? domainWideDelegationCredentialId : undefined + delegatedCalendar ? undefined : credentialId, + delegatedCalendar ? delegationCredentialId : undefined ); return { diff --git a/apps/api/v2/src/modules/organizations/dwd/inputs/create-dwd.input.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/inputs/create-delegation-credential.input.ts similarity index 86% rename from apps/api/v2/src/modules/organizations/dwd/inputs/create-dwd.input.ts rename to apps/api/v2/src/modules/organizations/delegation-credentials/inputs/create-delegation-credential.input.ts index 81c2f078b0..939c7973cb 100644 --- a/apps/api/v2/src/modules/organizations/dwd/inputs/create-dwd.input.ts +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/inputs/create-delegation-credential.input.ts @@ -2,12 +2,12 @@ import { GoogleServiceAccountKeyInput, ServiceAccountKeyValidator, MicrosoftServiceAccountKeyInput, -} from "@/modules/organizations/dwd/inputs/service-account-key.input"; +} from "@/modules/organizations/delegation-credentials/inputs/service-account-key.input"; import { ApiProperty, getSchemaPath } from "@nestjs/swagger"; import { Expose, Type } from "class-transformer"; import { IsString, IsNotEmpty, ValidateNested, Validate } from "class-validator"; -export class CreateDwdInput { +export class CreateDelegationCredentialInput { @IsString() @IsNotEmpty() @ApiProperty() diff --git a/apps/api/v2/src/modules/organizations/dwd/inputs/service-account-key.input.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/inputs/service-account-key.input.ts similarity index 100% rename from apps/api/v2/src/modules/organizations/dwd/inputs/service-account-key.input.ts rename to apps/api/v2/src/modules/organizations/delegation-credentials/inputs/service-account-key.input.ts diff --git a/apps/api/v2/src/modules/organizations/dwd/inputs/update-dwd.input.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/inputs/update-delegation-credential.input.ts similarity index 86% rename from apps/api/v2/src/modules/organizations/dwd/inputs/update-dwd.input.ts rename to apps/api/v2/src/modules/organizations/delegation-credentials/inputs/update-delegation-credential.input.ts index e87b37685d..2e12647634 100644 --- a/apps/api/v2/src/modules/organizations/dwd/inputs/update-dwd.input.ts +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/inputs/update-delegation-credential.input.ts @@ -2,12 +2,12 @@ import { GoogleServiceAccountKeyInput, MicrosoftServiceAccountKeyInput, ServiceAccountKeyValidator, -} from "@/modules/organizations/dwd/inputs/service-account-key.input"; +} from "@/modules/organizations/delegation-credentials/inputs/service-account-key.input"; import { ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger"; import { Expose, plainToClass, Transform, Type } from "class-transformer"; import { IsBoolean, IsOptional, Validate, ValidateNested } from "class-validator"; -export class UpdateDwdInput { +export class UpdateDelegationCredentialInput { @IsBoolean() @IsOptional() @ApiPropertyOptional() diff --git a/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.controller.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.controller.ts similarity index 50% rename from apps/api/v2/src/modules/organizations/dwd/organizations-dwd.controller.ts rename to apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.controller.ts index d5e6344015..ccf182609a 100644 --- a/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.controller.ts +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.controller.ts @@ -7,11 +7,11 @@ import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.g import { IsAdminAPIEnabledGuard } from "@/modules/auth/guards/organizations/is-admin-api-enabled.guard"; import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard"; import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard"; -import { UpdateDwdInput } from "@/modules/organizations/dwd/inputs/update-dwd.input"; -import { CreateDwdOutput } from "@/modules/organizations/dwd/outputs/create-dwd.output"; -import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output"; -import { UpdateDwdOutput } from "@/modules/organizations/dwd/outputs/update-dwd.output"; -import { OrganizationsDwdService } from "@/modules/organizations/dwd/services/organizations-dwd.service"; +import { UpdateDelegationCredentialInput } from "@/modules/organizations/delegation-credentials/inputs/update-delegation-credential.input"; +import { CreateDelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/create-delegation-credential.output"; +import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output"; +import { UpdateDelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/update-delegation-credential.output"; +import { OrganizationsDelegationCredentialService } from "@/modules/organizations/delegation-credentials/services/organizations-delegation-credential.service"; import { Controller, UseGuards, @@ -29,7 +29,7 @@ import { plainToClass } from "class-transformer"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; -import { CreateDwdInput } from "./inputs/create-dwd.input"; +import { CreateDelegationCredentialInput } from "./inputs/create-delegation-credential.input"; @Controller({ path: "/v2/organizations/:orgId/delegation-credentials", @@ -37,23 +37,27 @@ import { CreateDwdInput } from "./inputs/create-dwd.input"; }) @UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard) @DocsTags("Orgs / Delegation Credentials") -export class OrganizationsDwdController { - constructor(private readonly dwdService: OrganizationsDwdService) {} +export class OrganizationsDelegationCredentialController { + constructor(private readonly delegationCredentialService: OrganizationsDelegationCredentialService) {} @Post("/") @HttpCode(HttpStatus.CREATED) @Roles("ORG_ADMIN") @PlatformPlan("SCALE") @ApiOperation({ summary: "Save delegation credentials for your organization." }) - async createDwd( + async createDelegationCredential( @Param("orgId", ParseIntPipe) orgId: number, - @GetUser() dwdServiceAccountUser: User, - @Body() body: CreateDwdInput - ): Promise { - const dwd = await this.dwdService.createDwd(orgId, dwdServiceAccountUser, body); + @GetUser() delegatedServiceAccountUser: User, + @Body() body: CreateDelegationCredentialInput + ): Promise { + const delegationCredential = await this.delegationCredentialService.createDelegationCredential( + orgId, + delegatedServiceAccountUser, + body + ); return { status: SUCCESS_STATUS, - data: plainToClass(DwdOutput, dwd, { strategy: "excludeAll" }), + data: plainToClass(DelegationCredentialOutput, delegationCredential, { strategy: "excludeAll" }), }; } @@ -61,16 +65,21 @@ export class OrganizationsDwdController { @Roles("ORG_ADMIN") @PlatformPlan("SCALE") @ApiOperation({ summary: "Update delegation credentials of your organization." }) - async updateDwd( + async updateDelegationCredential( @Param("orgId", ParseIntPipe) orgId: number, - @GetUser() dwdServiceAccountUser: User, - @Body() body: UpdateDwdInput, + @GetUser() delegatedServiceAccountUser: User, + @Body() body: UpdateDelegationCredentialInput, @Param("credentialId") credentialId: string - ): Promise { - const dwd = await this.dwdService.updateDwd(orgId, credentialId, dwdServiceAccountUser, body); + ): Promise { + const delegationCredential = await this.delegationCredentialService.updateDelegationCredential( + orgId, + credentialId, + delegatedServiceAccountUser, + body + ); return { status: SUCCESS_STATUS, - data: plainToClass(DwdOutput, dwd, { strategy: "excludeAll" }), + data: plainToClass(DelegationCredentialOutput, delegationCredential, { strategy: "excludeAll" }), }; } } diff --git a/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.module.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.module.ts new file mode 100644 index 0000000000..a4fe8ebbf9 --- /dev/null +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.module.ts @@ -0,0 +1,21 @@ +import { MembershipsModule } from "@/modules/memberships/memberships.module"; +import { OrganizationsDelegationCredentialController } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.controller"; +import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository"; +import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository"; +import { PrismaModule } from "@/modules/prisma/prisma.module"; +import { RedisModule } from "@/modules/redis/redis.module"; +import { StripeModule } from "@/modules/stripe/stripe.module"; +import { Module } from "@nestjs/common"; + +import { OrganizationsDelegationCredentialService } from "./services/organizations-delegation-credential.service"; + +@Module({ + imports: [PrismaModule, StripeModule, RedisModule, MembershipsModule], + providers: [ + OrganizationsDelegationCredentialService, + OrganizationsDelegationCredentialRepository, + OrganizationsRepository, + ], + controllers: [OrganizationsDelegationCredentialController], +}) +export class OrganizationsDelegationCredentialModule {} diff --git a/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.repository.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.repository.ts new file mode 100644 index 0000000000..a5b3b447a3 --- /dev/null +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/organizations-delegation-credential.repository.ts @@ -0,0 +1,31 @@ +import { PrismaReadService } from "@/modules/prisma/prisma-read.service"; +import { Injectable } from "@nestjs/common"; + +import { Prisma } from "@calcom/prisma/client"; + +@Injectable() +export class OrganizationsDelegationCredentialRepository { + constructor(private readonly dbRead: PrismaReadService) {} + + async findById(delegationCredentialId: string) { + return this.dbRead.prisma.delegationCredential.findUnique({ where: { id: delegationCredentialId } }); + } + + async findByIdWithWorkspacePlatform(delegationCredentialId: string) { + return this.dbRead.prisma.delegationCredential.findUnique({ + where: { id: delegationCredentialId }, + include: { workspacePlatform: true }, + }); + } + + async updateIncludeWorkspacePlatform( + delegationCredentialId: string, + data: Prisma.DelegationCredentialUncheckedUpdateInput + ) { + return this.dbRead.prisma.delegationCredential.update({ + where: { id: delegationCredentialId }, + data, + include: { workspacePlatform: true }, + }); + } +} diff --git a/apps/api/v2/src/modules/organizations/dwd/outputs/update-dwd.output.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/outputs/create-delegation-credential.output.ts similarity index 62% rename from apps/api/v2/src/modules/organizations/dwd/outputs/update-dwd.output.ts rename to apps/api/v2/src/modules/organizations/delegation-credentials/outputs/create-delegation-credential.output.ts index 06feaa15d6..e0ca7b9c94 100644 --- a/apps/api/v2/src/modules/organizations/dwd/outputs/update-dwd.output.ts +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/outputs/create-delegation-credential.output.ts @@ -1,20 +1,20 @@ -import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output"; +import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output"; import { ApiProperty } from "@nestjs/swagger"; import { Type } from "class-transformer"; import { IsEnum, IsNotEmptyObject, ValidateNested } from "class-validator"; import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants"; -export class UpdateDwdOutput { +export class CreateDelegationCredentialOutput { @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) @IsEnum([SUCCESS_STATUS, ERROR_STATUS]) status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; @ApiProperty({ - type: DwdOutput, + type: DelegationCredentialOutput, }) @IsNotEmptyObject() @ValidateNested() - @Type(() => DwdOutput) - data!: DwdOutput; + @Type(() => DelegationCredentialOutput) + data!: DelegationCredentialOutput; } diff --git a/apps/api/v2/src/modules/organizations/dwd/outputs/dwd.output.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/outputs/delegation-credential.output.ts similarity index 95% rename from apps/api/v2/src/modules/organizations/dwd/outputs/dwd.output.ts rename to apps/api/v2/src/modules/organizations/delegation-credentials/outputs/delegation-credential.output.ts index 6a7247c9a3..c4a2fe54fa 100644 --- a/apps/api/v2/src/modules/organizations/dwd/outputs/dwd.output.ts +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/outputs/delegation-credential.output.ts @@ -14,7 +14,7 @@ class WorkspacePlatformDto { slug!: string; } -export class DwdOutput { +export class DelegationCredentialOutput { @ApiProperty() @IsString() @Expose() diff --git a/apps/api/v2/src/modules/organizations/dwd/outputs/create-dwd.output.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/outputs/update-delegation-credential.output.ts similarity index 62% rename from apps/api/v2/src/modules/organizations/dwd/outputs/create-dwd.output.ts rename to apps/api/v2/src/modules/organizations/delegation-credentials/outputs/update-delegation-credential.output.ts index 9651a7d08f..5033e96a7a 100644 --- a/apps/api/v2/src/modules/organizations/dwd/outputs/create-dwd.output.ts +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/outputs/update-delegation-credential.output.ts @@ -1,20 +1,20 @@ -import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output"; +import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output"; import { ApiProperty } from "@nestjs/swagger"; import { Type } from "class-transformer"; import { IsEnum, IsNotEmptyObject, ValidateNested } from "class-validator"; import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants"; -export class CreateDwdOutput { +export class UpdateDelegationCredentialOutput { @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) @IsEnum([SUCCESS_STATUS, ERROR_STATUS]) status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; @ApiProperty({ - type: DwdOutput, + type: DelegationCredentialOutput, }) @IsNotEmptyObject() @ValidateNested() - @Type(() => DwdOutput) - data!: DwdOutput; + @Type(() => DelegationCredentialOutput) + data!: DelegationCredentialOutput; } diff --git a/apps/api/v2/src/modules/organizations/delegation-credentials/services/organizations-delegation-credential.service.ts b/apps/api/v2/src/modules/organizations/delegation-credentials/services/organizations-delegation-credential.service.ts new file mode 100644 index 0000000000..62376ab4d6 --- /dev/null +++ b/apps/api/v2/src/modules/organizations/delegation-credentials/services/organizations-delegation-credential.service.ts @@ -0,0 +1,93 @@ +import { CreateDelegationCredentialInput } from "@/modules/organizations/delegation-credentials/inputs/create-delegation-credential.input"; +import { + GoogleServiceAccountKeyInput, + MicrosoftServiceAccountKeyInput, +} from "@/modules/organizations/delegation-credentials/inputs/service-account-key.input"; +import { UpdateDelegationCredentialInput } from "@/modules/organizations/delegation-credentials/inputs/update-delegation-credential.input"; +import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository"; +import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output"; +import { Injectable, NotFoundException } from "@nestjs/common"; +import { User } from "@prisma/client"; + +import { + addDelegationCredential, + toggleDelegationCredentialEnabled, + encryptServiceAccountKey, +} from "@calcom/platform-libraries"; + +@Injectable() +export class OrganizationsDelegationCredentialService { + constructor( + private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository + ) {} + async createDelegationCredential( + orgId: number, + delegatedServiceAccountUser: User, + body: CreateDelegationCredentialInput + ) { + const delegationCredential = await addDelegationCredential({ + input: body, + ctx: { user: { id: delegatedServiceAccountUser.id, organizationId: orgId } }, + }); + return delegationCredential; + } + + async updateDelegationCredential( + orgId: number, + delegationCredentialId: string, + delegatedServiceAccountUser: User, + body: UpdateDelegationCredentialInput + ) { + if (body.enabled !== undefined) { + await this.updateDelegationCredentialEnabled( + orgId, + delegationCredentialId, + delegatedServiceAccountUser, + body.enabled + ); + } + if (body.serviceAccountKey !== undefined) { + await this.updateDelegationCredentialServiceAccountKey(delegationCredentialId, body.serviceAccountKey); + } + const delegationCredential = + await this.organizationsDelegationCredentialRepository.findByIdWithWorkspacePlatform( + delegationCredentialId + ); + if (!delegationCredential) { + throw new NotFoundException(`DelegationCredential with id ${delegationCredentialId} not found`); + } + + return delegationCredential; + } + + async updateDelegationCredentialEnabled( + orgId: number, + delegationCredentialId: string, + delegatedServiceAccountUser: User, + enabled: boolean + ) { + const handlerUser = { + id: delegatedServiceAccountUser.id, + email: delegatedServiceAccountUser.email, + organizationId: orgId, + }; + const handlerBody = { id: delegationCredentialId, enabled }; + const delegationCredential = await toggleDelegationCredentialEnabled(handlerUser, handlerBody); + return delegationCredential; + } + + async updateDelegationCredentialServiceAccountKey( + delegationCredentialId: string, + serviceAccountKey: GoogleServiceAccountKeyInput | MicrosoftServiceAccountKeyInput + ) { + const encryptedServiceAccountKey = encryptServiceAccountKey(serviceAccountKey); + const delegationCredential = + await this.organizationsDelegationCredentialRepository.updateIncludeWorkspacePlatform( + delegationCredentialId, + { + serviceAccountKey: encryptedServiceAccountKey, + } + ); + return delegationCredential; + } +} diff --git a/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.module.ts b/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.module.ts deleted file mode 100644 index 72906a8f20..0000000000 --- a/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MembershipsModule } from "@/modules/memberships/memberships.module"; -import { OrganizationsDwdController } from "@/modules/organizations/dwd/organizations-dwd.controller"; -import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository"; -import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository"; -import { PrismaModule } from "@/modules/prisma/prisma.module"; -import { RedisModule } from "@/modules/redis/redis.module"; -import { StripeModule } from "@/modules/stripe/stripe.module"; -import { Module } from "@nestjs/common"; - -import { OrganizationsDwdService } from "./services/organizations-dwd.service"; - -@Module({ - imports: [PrismaModule, StripeModule, RedisModule, MembershipsModule], - providers: [OrganizationsDwdService, OrganizationsDwdRepository, OrganizationsRepository], - controllers: [OrganizationsDwdController], -}) -export class OrganizationsDwdModule {} diff --git a/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.repository.ts b/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.repository.ts deleted file mode 100644 index da537651ea..0000000000 --- a/apps/api/v2/src/modules/organizations/dwd/organizations-dwd.repository.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PrismaReadService } from "@/modules/prisma/prisma-read.service"; -import { Injectable } from "@nestjs/common"; - -import { Prisma } from "@calcom/prisma/client"; - -@Injectable() -export class OrganizationsDwdRepository { - constructor(private readonly dbRead: PrismaReadService) {} - - async findById(dwdId: string) { - return this.dbRead.prisma.domainWideDelegation.findUnique({ where: { id: dwdId } }); - } - - async findByIdWithWorkspacePlatform(dwdId: string) { - return this.dbRead.prisma.domainWideDelegation.findUnique({ - where: { id: dwdId }, - include: { workspacePlatform: true }, - }); - } - - async updateIncludeWorkspacePlatform(dwdId: string, data: Prisma.DomainWideDelegationUncheckedUpdateInput) { - return this.dbRead.prisma.domainWideDelegation.update({ - where: { id: dwdId }, - data, - include: { workspacePlatform: true }, - }); - } -} diff --git a/apps/api/v2/src/modules/organizations/dwd/services/organizations-dwd.service.ts b/apps/api/v2/src/modules/organizations/dwd/services/organizations-dwd.service.ts deleted file mode 100644 index 70914a4a1a..0000000000 --- a/apps/api/v2/src/modules/organizations/dwd/services/organizations-dwd.service.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { CreateDwdInput } from "@/modules/organizations/dwd/inputs/create-dwd.input"; -import { - GoogleServiceAccountKeyInput, - MicrosoftServiceAccountKeyInput, -} from "@/modules/organizations/dwd/inputs/service-account-key.input"; -import { UpdateDwdInput } from "@/modules/organizations/dwd/inputs/update-dwd.input"; -import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository"; -import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output"; -import { Injectable, NotFoundException } from "@nestjs/common"; -import { User } from "@prisma/client"; - -import { addDwd, toggleDwdEnabled, encryptServiceAccountKey } from "@calcom/platform-libraries"; - -@Injectable() -export class OrganizationsDwdService { - constructor(private readonly organizationsDwdRepository: OrganizationsDwdRepository) {} - async createDwd(orgId: number, dwdServiceAccountUser: User, body: CreateDwdInput) { - console.log("asap body", JSON.stringify(body, null, 2)); - const dwd = await addDwd({ - input: body, - ctx: { user: { id: dwdServiceAccountUser.id, organizationId: orgId } }, - }); - return dwd; - } - - async updateDwd(orgId: number, dwdId: string, dwdServiceAccountUser: User, body: UpdateDwdInput) { - if (body.enabled !== undefined) { - await this.updateDwdEnabled(orgId, dwdId, dwdServiceAccountUser, body.enabled); - } - if (body.serviceAccountKey !== undefined) { - await this.updateDwdServiceAccountKey(dwdId, body.serviceAccountKey); - } - const dwd = await this.organizationsDwdRepository.findByIdWithWorkspacePlatform(dwdId); - if (!dwd) { - throw new NotFoundException(`DWD with id ${dwdId} not found`); - } - - return dwd; - } - - async updateDwdEnabled(orgId: number, dwdId: string, dwdServiceAccountUser: User, enabled: boolean) { - const handlerUser = { - id: dwdServiceAccountUser.id, - email: dwdServiceAccountUser.email, - organizationId: orgId, - }; - const handlerBody = { id: dwdId, enabled }; - const dwd = await toggleDwdEnabled(handlerUser, handlerBody); - return dwd; - } - - async updateDwdServiceAccountKey( - dwdId: string, - serviceAccountKey: GoogleServiceAccountKeyInput | MicrosoftServiceAccountKeyInput - ) { - const encryptedServiceAccountKey = encryptServiceAccountKey(serviceAccountKey); - const dwd = await this.organizationsDwdRepository.updateIncludeWorkspacePlatform(dwdId, { - serviceAccountKey: encryptedServiceAccountKey, - }); - return dwd; - } -} diff --git a/apps/api/v2/src/modules/organizations/organizations.module.ts b/apps/api/v2/src/modules/organizations/organizations.module.ts index d468faea35..86f1ca89cd 100644 --- a/apps/api/v2/src/modules/organizations/organizations.module.ts +++ b/apps/api/v2/src/modules/organizations/organizations.module.ts @@ -11,7 +11,7 @@ import { OrganizationAttributesService } from "@/modules/organizations/attribute import { OrganizationAttributeOptionRepository } from "@/modules/organizations/attributes/options/organization-attribute-options.repository"; import { OrganizationsAttributesOptionsController } from "@/modules/organizations/attributes/options/organizations-attributes-options.controller"; import { OrganizationAttributeOptionService } from "@/modules/organizations/attributes/options/services/organization-attributes-option.service"; -import { OrganizationsDwdModule } from "@/modules/organizations/dwd/organizations-dwd.module"; +import { OrganizationsDelegationCredentialModule } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.module"; import { OrganizationsEventTypesController } from "@/modules/organizations/event-types/organizations-event-types.controller"; import { OrganizationsEventTypesRepository } from "@/modules/organizations/event-types/organizations-event-types.repository"; import { OutputTeamEventTypesResponsePipe } from "@/modules/organizations/event-types/pipes/team-event-types-response.transformer"; @@ -65,7 +65,7 @@ import { Module } from "@nestjs/common"; EventTypesModule_2024_06_14, TeamsEventTypesModule, TeamsModule, - OrganizationsDwdModule, + OrganizationsDelegationCredentialModule, OrganizationsOrganizationsModule, OrganizationsTeamsRoutingFormsModule, ], diff --git a/apps/api/v2/src/modules/selected-calendars/inputs/selected-calendars.input.ts b/apps/api/v2/src/modules/selected-calendars/inputs/selected-calendars.input.ts index 3d24593667..50d5e9e138 100644 --- a/apps/api/v2/src/modules/selected-calendars/inputs/selected-calendars.input.ts +++ b/apps/api/v2/src/modules/selected-calendars/inputs/selected-calendars.input.ts @@ -12,7 +12,7 @@ export class SelectedCalendarsInputDto { @IsString() @IsOptional() - readonly domainWideDelegationCredentialId?: string; + readonly delegationCredentialId?: string; } export class SelectedCalendarsQueryParamsInputDto { @@ -27,5 +27,5 @@ export class SelectedCalendarsQueryParamsInputDto { @IsString() @IsOptional() - readonly domainWideDelegationCredentialId?: string; + readonly delegationCredentialId?: string; } diff --git a/apps/api/v2/src/modules/selected-calendars/selected-calendars.module.ts b/apps/api/v2/src/modules/selected-calendars/selected-calendars.module.ts index 0665bb53a7..da2beb8258 100644 --- a/apps/api/v2/src/modules/selected-calendars/selected-calendars.module.ts +++ b/apps/api/v2/src/modules/selected-calendars/selected-calendars.module.ts @@ -2,7 +2,7 @@ import { CalendarsRepository } from "@/ee/calendars/calendars.repository"; import { CalendarsService } from "@/ee/calendars/services/calendars.service"; import { AppsRepository } from "@/modules/apps/apps.repository"; import { CredentialsRepository } from "@/modules/credentials/credentials.repository"; -import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository"; +import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository"; import { OrganizationsMembershipRepository } from "@/modules/organizations/memberships/organizations-membership.repository"; import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service"; import { PrismaModule } from "@/modules/prisma/prisma.module"; @@ -22,7 +22,7 @@ import { Module } from "@nestjs/common"; CredentialsRepository, AppsRepository, OrganizationsMembershipService, - OrganizationsDwdRepository, + OrganizationsDelegationCredentialRepository, OrganizationsMembershipRepository, SelectedCalendarsService, ], diff --git a/apps/api/v2/src/modules/selected-calendars/selected-calendars.repository.ts b/apps/api/v2/src/modules/selected-calendars/selected-calendars.repository.ts index 7f9b17eeac..5dd45ea19b 100644 --- a/apps/api/v2/src/modules/selected-calendars/selected-calendars.repository.ts +++ b/apps/api/v2/src/modules/selected-calendars/selected-calendars.repository.ts @@ -89,7 +89,7 @@ export class SelectedCalendarsRepository { userId: number, integration: string, externalId: string, - domainWideDelegationCredentialId?: string + delegationCredentialId?: string ) { // Using deleteMany because userId_externalId_integration_eventTypeId is a unique constraint but with eventTypeId being nullable, causing it to be not used as a unique constraint const records = await this.dbWrite.prisma.selectedCalendar.findMany({ @@ -97,7 +97,7 @@ export class SelectedCalendarsRepository { userId, externalId, integration, - domainWideDelegationCredentialId, + delegationCredentialId, ...ensureUserLevelWhere, }, }); diff --git a/apps/api/v2/src/modules/selected-calendars/services/selected-calendars.service.ts b/apps/api/v2/src/modules/selected-calendars/services/selected-calendars.service.ts index ac94840bd8..bc2f153f43 100644 --- a/apps/api/v2/src/modules/selected-calendars/services/selected-calendars.service.ts +++ b/apps/api/v2/src/modules/selected-calendars/services/selected-calendars.service.ts @@ -1,5 +1,5 @@ import { CalendarsService } from "@/ee/calendars/services/calendars.service"; -import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository"; +import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository"; import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service"; import { SelectedCalendarsInputDto, @@ -11,7 +11,9 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { SelectedCalendarRepository } from "@calcom/platform-libraries"; -type SelectedCalendarsInputDwd = SelectedCalendarsInputDto & { domainWideDelegationCredentialId: string }; +type SelectedCalendarsInputDelegationCredential = SelectedCalendarsInputDto & { + delegationCredentialId: string; +}; @Injectable() export class SelectedCalendarsService { @@ -19,16 +21,16 @@ export class SelectedCalendarsService { private readonly calendarsService: CalendarsService, private readonly selectedCalendarsRepository: SelectedCalendarsRepository, private readonly organizationsMembershipService: OrganizationsMembershipService, - private readonly organizationsDwdRepository: OrganizationsDwdRepository + private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository ) {} async addSelectedCalendar(user: UserWithProfile, input: SelectedCalendarsInputDto) { - if (input.domainWideDelegationCredentialId) { - const dwdInput = { + if (input.delegationCredentialId) { + const delegationCredentialInput = { ...input, - domainWideDelegationCredentialId: input.domainWideDelegationCredentialId, + delegationCredentialId: input.delegationCredentialId, }; - return this.addSelectedCalendarDwd(user, dwdInput); + return this.addSelectedCalendarDelegationCredential(user, delegationCredentialInput); } return this.addSelectedCalendarUser(user, input); } @@ -47,35 +49,42 @@ export class SelectedCalendarsService { return userSelectedCalendar; } - private async addSelectedCalendarDwd(user: UserWithProfile, selectedCalendar: SelectedCalendarsInputDwd) { - const isMemberOfOrganization = await this.isMemberOfDwdOrganization( + private async addSelectedCalendarDelegationCredential( + user: UserWithProfile, + selectedCalendar: SelectedCalendarsInputDelegationCredential + ) { + const isMemberOfOrganization = await this.isMemberOfDelegationCredentialOrganization( user.id, - selectedCalendar.domainWideDelegationCredentialId + selectedCalendar.delegationCredentialId ); if (!isMemberOfOrganization) { - throw new NotFoundException("User is not a member of the organization that owns the DWD credential"); + throw new NotFoundException( + "User is not a member of the organization that owns the Delegation credential" + ); } - const { integration, externalId, credentialId, domainWideDelegationCredentialId } = selectedCalendar; - const dwdSelectedCalendar = await SelectedCalendarRepository.upsert({ + const { integration, externalId, credentialId, delegationCredentialId } = selectedCalendar; + const delegationCredentialSelectedCalendar = await SelectedCalendarRepository.upsert({ userId: user.id, integration, externalId, credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, eventTypeId: null, }); - return dwdSelectedCalendar; + return delegationCredentialSelectedCalendar; } - private async isMemberOfDwdOrganization(userId: number, domainWideDelegationCredentialId: string) { - const dwd = await this.organizationsDwdRepository.findById(domainWideDelegationCredentialId); - if (!dwd) { - throw new NotFoundException("DWD with provided domainWideDelegationCredentialId not found"); + private async isMemberOfDelegationCredentialOrganization(userId: number, delegationCredentialId: string) { + const delegationCredential = await this.organizationsDelegationCredentialRepository.findById( + delegationCredentialId + ); + if (!delegationCredential) { + throw new NotFoundException("DelegationCredential with provided delegationCredentialId not found"); } const isMemberOfOrganization = await this.organizationsMembershipService.getOrgMembershipByUserId( - dwd.organizationId, + delegationCredential.organizationId, userId ); @@ -86,17 +95,19 @@ export class SelectedCalendarsService { selectedCalendar: SelectedCalendarsQueryParamsInputDto, user: UserWithProfile ) { - const { integration, externalId, credentialId, domainWideDelegationCredentialId } = selectedCalendar; + const { integration, externalId, credentialId, delegationCredentialId } = selectedCalendar; - if (!domainWideDelegationCredentialId) { + if (!delegationCredentialId) { await this.calendarsService.checkCalendarCredentials(Number(credentialId), user.id); } else { - const isMemberOfOrganization = await this.isMemberOfDwdOrganization( + const isMemberOfOrganization = await this.isMemberOfDelegationCredentialOrganization( user.id, - domainWideDelegationCredentialId + delegationCredentialId ); if (!isMemberOfOrganization) { - throw new NotFoundException("User is not a member of the organization that owns the DWD credential"); + throw new NotFoundException( + "User is not a member of the organization that owns the Delegation credential" + ); } } @@ -104,7 +115,7 @@ export class SelectedCalendarsService { user.id, integration, externalId, - domainWideDelegationCredentialId + delegationCredentialId ); return removedCalendarEntry; diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index b8eb08086f..bdf812f1d9 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -1093,7 +1093,7 @@ }, "/v2/organizations/{orgId}/delegation-credentials": { "post": { - "operationId": "OrganizationsDwdController_createDwd", + "operationId": "OrganizationsDelegationCredentialController_createDelegationCredential", "summary": "Save delegation credentials for your organization.", "parameters": [ { @@ -1110,7 +1110,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateDwdInput" + "$ref": "#/components/schemas/CreateDelegationCredentialInput" } } } @@ -1121,7 +1121,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateDwdOutput" + "$ref": "#/components/schemas/CreateDelegationCredentialOutput" } } } @@ -1134,7 +1134,7 @@ }, "/v2/organizations/{orgId}/delegation-credentials/{credentialId}": { "patch": { - "operationId": "OrganizationsDwdController_updateDwd", + "operationId": "OrganizationsDelegationCredentialController_updateDelegationCredential", "summary": "Update delegation credentials of your organization.", "parameters": [ { @@ -1159,7 +1159,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateDwdInput" + "$ref": "#/components/schemas/UpdateDelegationCredentialInput" } } } @@ -1170,7 +1170,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateDwdOutput" + "$ref": "#/components/schemas/UpdateDelegationCredentialOutput" } } } @@ -5942,7 +5942,7 @@ } }, { - "name": "domainWideDelegationCredentialId", + "name": "delegationCredentialId", "required": false, "in": "query", "schema": { @@ -11499,7 +11499,7 @@ "credentialId": { "type": "number" }, - "domainWideDelegationCredentialId": { + "delegationCredentialId": { "type": "string" } }, @@ -15170,7 +15170,7 @@ "client_id" ] }, - "CreateDwdInput": { + "CreateDelegationCredentialInput": { "type": "object", "properties": { "workspacePlatformSlug": { @@ -15214,7 +15214,7 @@ "slug" ] }, - "DwdOutput": { + "DelegationCredentialOutput": { "type": "object", "properties": { "id": { @@ -15251,7 +15251,7 @@ "updatedAt" ] }, - "CreateDwdOutput": { + "CreateDelegationCredentialOutput": { "type": "object", "properties": { "status": { @@ -15263,7 +15263,7 @@ ] }, "data": { - "$ref": "#/components/schemas/DwdOutput" + "$ref": "#/components/schemas/DelegationCredentialOutput" } }, "required": [ @@ -15271,7 +15271,7 @@ "data" ] }, - "UpdateDwdInput": { + "UpdateDelegationCredentialInput": { "type": "object", "properties": { "enabled": { @@ -15292,7 +15292,7 @@ } } }, - "UpdateDwdOutput": { + "UpdateDelegationCredentialOutput": { "type": "object", "properties": { "status": { @@ -15304,7 +15304,7 @@ ] }, "data": { - "$ref": "#/components/schemas/DwdOutput" + "$ref": "#/components/schemas/DelegationCredentialOutput" } }, "required": [ @@ -15914,7 +15914,7 @@ "credentialId": { "type": "number" }, - "domainWideDelegationCredentialId": { + "delegationCredentialId": { "type": "string", "nullable": true } @@ -15955,7 +15955,7 @@ "credentialId": { "type": "number" }, - "domainWideDelegationCredentialId": { + "delegationCredentialId": { "type": "string", "nullable": true } @@ -15976,7 +15976,7 @@ "credentialId": { "type": "number" }, - "domainWideDelegationCredentialId": { + "delegationCredentialId": { "type": "string", "nullable": true }, @@ -16023,7 +16023,7 @@ "type": "number", "nullable": true }, - "domainWideDelegationCredentialId": { + "delegationCredentialId": { "type": "string", "nullable": true }, @@ -18383,7 +18383,7 @@ "example": "https://caldav.icloud.com/26962146906/calendars/1644422A-1945-4438-BBC0-4F0Q23A57R7S/", "description": "Unique identifier used to represent the specfic calendar, as returned by the /calendars endpoint" }, - "domainWideDelegationCredentialId": { + "delegationCredentialId": { "type": "string" } }, diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx index a59dfdbe3b..aacc629b61 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx @@ -168,10 +168,10 @@ const organizationAdminKeys = [ "OAuth Clients", "SSO", "directory_sync", - "domain_wide_delegation", + "delegation_credential", ]; -const useTabs = ({ isDwdEnabled }: { isDwdEnabled: boolean }) => { +const useTabs = ({ isDelegationCredentialEnabled }: { isDelegationCredentialEnabled: boolean }) => { const session = useSession(); const { data: user } = trpc.viewer.me.useQuery({ includePasswordAdded: true }); const orgBranding = useOrgBranding(); @@ -202,11 +202,11 @@ const useTabs = ({ isDwdEnabled }: { isDwdEnabled: boolean }) => { }); } - // Add domain-wide-delegation menu item only if feature flag is enabled - if (isDwdEnabled) { + // Add delegation-credential menu item only if feature flag is enabled + if (isDelegationCredentialEnabled) { newArray.push({ - name: "domain_wide_delegation", - href: "/settings/organizations/domain-wide-delegation", + name: "delegation_credential", + href: "/settings/organizations/delegation-credential", }); } @@ -243,7 +243,7 @@ const useTabs = ({ isDwdEnabled }: { isDwdEnabled: boolean }) => { if (isAdmin) return true; return !adminRequiredKeys.includes(tab.name); }); - }, [isAdmin, orgBranding, isOrgAdminOrOwner, user, isDwdEnabled]); + }, [isAdmin, orgBranding, isOrgAdminOrOwner, user, isDelegationCredentialEnabled]); return processTabsMemod; }; @@ -440,7 +440,9 @@ const SettingsSidebarContainer = ({ enabled: !!session.data?.user?.org && !currentOrgProp, }); - const tabsWithPermissions = useTabs({ isDwdEnabled: !!_currentOrg?.features?.domainWideDelegation }); + const tabsWithPermissions = useTabs({ + isDelegationCredentialEnabled: !!_currentOrg?.features?.delegationCredential, + }); const { data: _otherTeams } = trpc.viewer.organizations.listOtherTeams.useQuery(undefined, { enabled: !!session.data?.user?.org && !otherTeamsProp, diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/delegation-credential/page.tsx similarity index 53% rename from apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/page.tsx rename to apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/delegation-credential/page.tsx index 5f72eaeec1..f76f791580 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/page.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/delegation-credential/page.tsx @@ -1,12 +1,12 @@ import { _generateMetadata, getTranslate } from "app/_utils"; -import DomainWideDelegationList from "@calcom/features/ee/organizations/pages/settings/domainWideDelegation"; +import DelegationCredentialList from "@calcom/features/ee/organizations/pages/settings/delegationCredential"; import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; export const generateMetadata = async () => await _generateMetadata( - (t) => t("domain_wide_delegation"), - (t) => t("domain_wide_delegation_description") + (t) => t("delegation_credential"), + (t) => t("delegation_credential_description") ); const Page = async () => { @@ -15,9 +15,9 @@ const Page = async () => { return ( - + title={t("delegation_credential")} + description={t("delegation_credential_description")}> + ); }; diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/domain-wide-delegation-TODO.md b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/domain-wide-delegation-TODO.md deleted file mode 100644 index 10d76ca090..0000000000 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/domain-wide-delegation-TODO.md +++ /dev/null @@ -1,112 +0,0 @@ -## Version 1.0 -### Release Plan - 1. Read the document(domain-wide-delegation.md) and acknowledge it. - 3. Deploy: - 1. Follow "Setting up Domain-Wide Delegation for Google Calendar API" in domain-wide-delegation.md to create Service Account and create a workspace. - 2. Merge PR(without Calendar Cache Support) and then deploy. - 4. Enable for i.cal.com: - 1. Disable Calendar Cache for i.cal.com - 2. Enable DWD for i.cal.com first and then test there - 3. Wait for 1-2 days and keep monitoring the errors in Sentry and Axiom. - 5. Merge & Deploy the Calendar Cache support(with DWD) PR - 1. Enable Calendar Cache back for i.cal.com - 2. Observe the errors in Sentry and Axiom. - 6. Enable for a big customer: - 1. Wait for a week and keep monitoring the errors in Sentry and Axiom. - 7. Use delegatedCredentialsFirst instead of delegatedCredentialsLast in EventManager.ts - - Observe errors in Sentry and Axiom. - -## Manual Testing - - V2/V1 APIs - There could be problem if DWD credentials aren't supported there. Because as organization enables DWD and new users won't need to connect their calendars in the old way. e.g. getBusyCalendarTimes seem to be used there in V2. - - [x] Request reshedule - cancellation of meeting in calendar verification - - [x] Isolation of DWD credentials for different organizations - - [x] Using same domain name for different organization isn't allowed. It is restricted during creation of DWD. - - [x] Location Change of a booking to Google Meet(from Cal Video) - - [x] Google Meet/Google Calendar - - [x] Shows up installed on apps/google-meet and apps/instaled/conferencing - - [x] Can't remove the app - - [x] Seated Event - - [x] Seat cancellation removes calendar invite - - [x] Onboarding - - [x] When DWD is not enabled, the flow works. - - [x] When DWD is enabled, Google Calendar is pre-installed and Destination Calendar and Selected Calendar are configurable. On next step, Google Meet is pre-installed and shown at the top and could be set as default. - - [x] User Level Selected Calendar and Destination Calendars are honored - verified through booking the user. - - [ ] Event Type Selected Calendar and Destination Calendar - - [ ] Dynamic Group Booking - - [x] Owner must have verified email to enable DWD - - [x] RR Team Event - - [x] Booking - - Unavailable slot isn't available for booking. Unavailable user isn't used. - - [x] Reroute - - [x] Reassign - - [x] Troubleshooter - - [x] Shows busy times from Calendar - - [ ] Calendar Cache[TO BE TESTED LATER] - - [ ] Event Type Selected Calendar caching test - - [ ] User Selected Calendar caching test - -### Important - - **Performance** - - There could be 100s of users in an organization with already connected calendars. Enabling DWD adds a duplicate credential(in-memory) for each of them. - - Because a credential isn't aware of which externalIds it has access to(without connecting with Google Calendar API itself), we can't identify which credential is for which SelectedCalendar and thus we can't de-duplicate them. This has an impact on fetching the availability and thus for a team event with x participants, we could have 2x requests to Google Calendar API. Because for big value of x, user might be using routing already with team members and thus the actual value of x might be much lower. So, we could be fine. Also, we would have Calendar Cache enabled there already to reduce the requests. - - Bugs - - [x] For RR event, if I am able to see the slots for a blocked day in calendar(by temporarily unselecting the calendar), and then we enable the calendar back, the blocked day can be booked. - - [x] Not using Goole Meet with Default conferencing app. - - [x] Duplicate Calendar Events in Google Calendar when choosing non-primary calendar as destination. No idea why this is happening. - - The issue was in getAttendees in Google CalendarService not using externalCalendarId. - - [x] Restrict toggling based on email verified - - [x] Duplicate Calendar connections in 'apps/installed' if a user already had connected calendar and DWD is enabled. - - [x] Calendar Cache has credentialId column which isn't applicable for DWD(Solution: Added userId there) - - [x] Troubleshooter - - [x] Shows busy times from Claendar - - [x] If a user has connected a calendar, and then DWD is enabled. - - Tested various scenarios for it - - [x] Inviting a new user. - - Verified that Google Calendar is shown pre-installed. - - How about Google Meet(which depends on Google Calendar) - Correctly shows up as installed. - - TODO: - - [ ] Add experimental/Beta flag in DWD - - [ ] Consideration of multiple domains email in a single team event. We might need to consider all the member's domains in the team event when fetching availabilituy. - - [x] Performance - - [x] Available Slots and booking flow shouldn't slow down. Right now the querying logic is not optimized. We query per team member, we should do one query - This is fixed and took quite a refactor but it is cleaner code now. - - [ ] Tag all DWD related logs and errors with "DWD:" - - [x] Troubleshooter - - [x] Google CalendarService unit tests to verify that if DWD credential is provided it uses impersonation to access API otherwise it uses regular user credential API. - - [x] setDestinationCalendar.handler.ts tests to verify that when DWD is enabled it still correctly sets the destination calendar. - - [x] getConnectedDestinationCalendars tests. - - [x] Creating DWD shouldn't immediately enable it. Enabling has separate check to confirm if it is actually configured in google workspace - - [x] Added check to avoid adding same domain for a workspace platform in another organization if it is already enabled in some other organization - - [x] Don't show dwd in menu for non-org-admin users - It errors with something_went_wrong right now - - [x] Don't allow disabled platform to be selected in the UI for creation. - - We have disabled coming the disabled platform to be coming into the list that effectively disables edit of existing dwd and creation of new dwd for that platform. - - [x] Where should we show the user the client ID to enable domain wide delegation? - - [x] It must be shown to the organization owner/admin only - - [x] There could be multiple checkboxes per domain to enable domain wide delegation for a domain - - [x] Which domain to allow - - Any domain can be added by a user - - [x] Support multiple domains in DomainWideDelegation schema for an organization - - [x] Use the domain as well to identify if the domain wide delegation is enabled - - [x] Before enabling Domain-wide delegation, there should be a check to ensure that the clientID has been added to the Workspace Platform - - [x] We should allow setting default conferencing app during onboarding - -### Follow-up release - - [ ] Confirmation for DwD toggling off - - [ ] Confirmation for DwD deletion - Deletion isn't there at the moment. - - [ ] Make Google Meet "show up" as default conferencing app when DWD for Google is enabled. - - [ ] Profile pic from Google with DWD might not be working - Fix it. - -### Security - - [x] We don't let any one user see the added service account key from UI. - - [ ] We intend to implement Workload Identity Federation in the future. - -### Documentation -- After enabling domain-wide delegation, the credential is shown pre-installed and the connection can't be removed(or the app can't be uninstalled by user) -- Steps - - App admin will first create a Workspace Platform and then organization owner/admin can enable domain-wide delegation for a domain - - As soon as domain-wide delegation is created, it would start taking preference over the personal credentials of the organization members and it would be used for that. - -Version-2.0 -- Workload Identity Federation to ensure that the service account key is never stored in DB. - - - diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/domain-wide-delegation.md b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/domain-wide-delegation.md deleted file mode 100644 index 157162ee5b..0000000000 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/domain-wide-delegation/domain-wide-delegation.md +++ /dev/null @@ -1,104 +0,0 @@ -## Setting up Domain-Wide Delegation for Google Calendar API - -Step 1: Create a Google Cloud Project - -Before you can create a service account, you'll need to set up a Google Cloud project. - - 1. Create a Google Cloud Project: - - Go to the Google Cloud Console - - Select Create Project - - Give your project a name and select your billing account (if applicable) - - Click Create - 2. Enable the Google Calendar API: - 1. Go to the Google Cloud Console - 2. Select API & Services → Library - 3. Search for "Google Calendar API" - 4. Click Enable - -Step 2: Create a Service Account - -A service account is needed to act on behalf of users - - 1. Navigate to the Service Accounts page: - - In the Google Cloud Console, go to IAM & Admin → Service Accounts - 2. Create a New Service Account: - - Click on Create Service Account - - Give your service account a name and description - - Click Create and Continue - -Step 3: To Be taken by Cal.com instance admin: - - Create a Workspace Platform with slug="google". Slug has to be exactly this. This is how we know we need to use Google Calendar and Google Meet. - -Last Step (To Be Taken By Cal.com organization Owner/Admin): Assign Specific API Permissions via OAuth Scopes: - - Create DWD with workspace platform "google" - - User must be a member of the Google Workspace to be able to enable DWD as there is a validation if the user's calendar can be accessed through the service account - - Get the Client ID from there - - Go to your Google Admin Console (admin-google-com) - - Navigate to Security → Access and Data Controls -> API controls -> Manage Domain-Wide Delegation - - Here, you'll authorize the Client ID(Unique ID) to access the Google Calendar API - - Add the necessary API scopes for Google Calendar(Full access to Google Calendar) - https://www.googleapis.com/auth/calendar - - -## Onboarding Improvement -- Just adding a member to the organization would do the following: - - Member to receive events in their calendar, even if they don't login to their account and complete the onboarding process. - - The booking location would be Google Meet, even if the user hasn't set it as default(Though Cal Video would show up as default, but we still use Google Meet in this case. We will fix it later.) - - It would still not use their calendar for conflict checking, but user can complete the onboarding(just select one calendar there for conflict checking) -- Onboarding process: Google Calendar is pre-installed for any new member of the organization(assuming the user has an email of the DWD domain) and Destination Calendar and Selected Calendar are configurable. On next step, Google Meet is pre-installed and shown at the top and could be set as default. - -## Restrictions after enabling DWD -- Enabling DWD for a particular workspace in Cal.com(only google supported at the moment) disables the user from disconnecting that credential. - -## Who can create DWD and enable it? -- Only the owner/admin of the organization can create DWD -- Only the owner/admin of the organization can enable the created DWD. Following additional requirements are there: - - The client ID must be added to the Google Admin Console - - The user's email must be a member of the Google Workspace - - The user's email must be verified. - -## Disabling DWD - - Disabling DWD maintains the user's preferences in terms of SelectedCalendars and DestinationCalendar. - -## Developer Notes -### Terminology -- DWD Credential: A DWD service account key along with user's email becomes the DWD Credential which is an alternative to regular Credential in DB. -- DWD: Domain Wide Delegation -- non-dwd credential: Regular credentials that are stored in Credentials table - -### How DWD works -- We use the Cal.com user's email to impersonate that user using DWD Credential(which is just a service account key at the moment) - - That gives us read/write permission to get availability of the user and create new events in their calendar. - -### What is a DWD Credential? -- A DWD service account key along with user's email becomes the DWD Credential which is an alternative to regular Credential in DB. -- DWD doesn't completely replace the regular credentials. DWD Credential gives access to the cal.com user's email in Google Calendar. So, if the user needs to connect to some other email's calendar, we need to use the regular credentials. - -### Important Points -- No Credential table entry is created when enabling DWD. The workspace platform's related apps will be considered as "installed" for the users with email matching dwd domain. An in-memory credential like object is created for this purpose. It allows avoiding creation of thousands of records for all the members of the organization when dwd is enabled. -- DWD Credential is applicable to Users only. - - For team, we don't use dwd credential as you can impersonate a user and not team through Dwd credential. Currently supported apps(Google Calendar and Google Meet) don't support team installation, so we could simply allow enabling DWD without any issues. -- Disabling a workspace platform stops it from being used for any new organizations and also disables any DWD using the workspace platform from being edited. - - It still all existing DWDs to keep on working -- Adding any number of DWDs for a particular workspace always gives the same Client ID as DWD uses the workspace's default Service Account. -- We should disable DWD and not delete it when we want to stop using it temporarily. Deleting DWD also removes all the seletedCalendar entries connected to it. - -### How apps/installed loads the credentials -1. Identify the logged in user's email -2. Identify the domainWideDelegations for that email's domain -3. Build in-memory credentials for the domainWideDelegations and use them along with the actual credentials(that user might have connected) of the user -4. We don't show the non-dwd connected calendar(if there is a corresponding dwd connected calendar). Though we use the non-dwd credentials to identify the selected calendars, for the dwd connected calendar. - -### Impact on existing users booking flow -- There should be no impact on availability on enabling DWD because we keep on using the existing credentials along with new DWD credential. -- When booking the event, we sort the credentials with DWD credentials last, so there should be no impact on creating calendar events. - - NOTE: We will followup with sorting the credentials with DWD credentials first in a followup PR(They are preferred because they don't expire) - -### Impact on APIs - [ To Verify ] -- We don't support DWD through APIs yet. So, all existing APIs would continue to work with non-dwd credentials only. - -### Notes when testing locally -- You need to enable the feature through feature flag. -- You could use Acme org and login as owner1-acme@example.com -- Make sure to change the email of the user above to your workspace owner's email(other member's email might also work). This is necessary otherwise you won't be able to enable DWD for the organization. - - Note: After changing the email, you would have to logout and login again as required by NextAuth \ No newline at end of file diff --git a/apps/web/app/api/availability/calendar/route.ts b/apps/web/app/api/availability/calendar/route.ts index 06bb7aa1f0..6ccc587a84 100644 --- a/apps/web/app/api/availability/calendar/route.ts +++ b/apps/web/app/api/availability/calendar/route.ts @@ -17,7 +17,7 @@ const selectedCalendarSelectSchema = z.object({ integration: z.string(), externalId: z.string(), credentialId: z.coerce.number(), - domainWideDelegationCredentialId: z.string().nullish().default(null), + delegationCredentialId: z.string().nullish().default(null), eventTypeId: z.coerce.number().nullish(), }); @@ -65,7 +65,7 @@ async function postHandler(req: NextRequest) { const user = await authMiddleware(); const body = await req.json(); - const { integration, externalId, credentialId, eventTypeId, domainWideDelegationCredentialId } = + const { integration, externalId, credentialId, eventTypeId, delegationCredentialId } = selectedCalendarSelectSchema.parse(body); await SelectedCalendarRepository.upsert({ @@ -73,7 +73,7 @@ async function postHandler(req: NextRequest) { integration, externalId, credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, eventTypeId: eventTypeId ?? null, }); diff --git a/apps/web/components/getting-started/components/AppConnectionItem.tsx b/apps/web/components/getting-started/components/AppConnectionItem.tsx index 174f1941fd..3066316b01 100644 --- a/apps/web/components/getting-started/components/AppConnectionItem.tsx +++ b/apps/web/components/getting-started/components/AppConnectionItem.tsx @@ -113,7 +113,7 @@ const AppConnectionItem = (props: IAppConnectionItem) => { )} /> - {/* It is possible that app is already installed here during onboarding due to Domain Wide Delegation enabled at organization level. We allow the user to set it as default */} + {/* It is possible that app is already installed here during onboarding due to Delegation Credential enabled at organization level. We allow the user to set it as default */} {installed && !isDefault && ( ); }; @@ -419,8 +419,8 @@ function DomainWideDelegationList() { ) : ( } /> @@ -446,6 +446,6 @@ function DomainWideDelegationList() { ); } -export default function DomainWideDelegationListPage() { - return ; +export default function DelegationCredentialListPage() { + return ; } diff --git a/packages/features/ee/round-robin/roundRobinManualReassignment.ts b/packages/features/ee/round-robin/roundRobinManualReassignment.ts index c9b1a1904a..4f3383e851 100644 --- a/packages/features/ee/round-robin/roundRobinManualReassignment.ts +++ b/packages/features/ee/round-robin/roundRobinManualReassignment.ts @@ -20,7 +20,7 @@ import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/rem import { isPrismaObjOrUndefined } from "@calcom/lib"; import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser"; import { SENDER_NAME } from "@calcom/lib/constants"; -import { enrichUserWithDwdCredentialsWithoutOrgId } from "@calcom/lib/domainWideDelegation/server"; +import { enrichUserWithDelegationCredentialsWithoutOrgId } from "@calcom/lib/delegationCredential/server"; import { getEventName } from "@calcom/lib/event"; import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server"; import logger from "@calcom/lib/logger"; @@ -275,7 +275,7 @@ export const roundRobinManualReassignment = async ({ include: { user: { select: { email: true } } }, }); - const newUserWithCredentials = await enrichUserWithDwdCredentialsWithoutOrgId({ + const newUserWithCredentials = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: { ...newUser, credentials }, }); diff --git a/packages/features/ee/round-robin/roundRobinReassignment.ts b/packages/features/ee/round-robin/roundRobinReassignment.ts index 2390a45239..304e2cf89c 100644 --- a/packages/features/ee/round-robin/roundRobinReassignment.ts +++ b/packages/features/ee/round-robin/roundRobinReassignment.ts @@ -22,9 +22,9 @@ import { isPrismaObjOrUndefined } from "@calcom/lib"; import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser"; import { SENDER_NAME } from "@calcom/lib/constants"; import { - enrichHostsWithDwdCredentials, - enrichUserWithDwdCredentialsWithoutOrgId, -} from "@calcom/lib/domainWideDelegation/server"; + enrichHostsWithDelegationCredentials, + enrichUserWithDelegationCredentialsWithoutOrgId, +} from "@calcom/lib/delegationCredential/server"; import { getEventName } from "@calcom/lib/event"; import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server"; import logger from "@calcom/lib/logger"; @@ -119,7 +119,7 @@ export const roundRobinReassignment = async ({ const previousRRHostT = await getTranslation(previousRRHost?.locale || "en", "common"); - const eventTypeHosts = await enrichHostsWithDwdCredentials({ + const eventTypeHosts = await enrichHostsWithDelegationCredentials({ orgId, hosts: eventType.hosts, }); @@ -320,7 +320,7 @@ export const roundRobinReassignment = async ({ }, }); - const organizerWithCredentials = await enrichUserWithDwdCredentialsWithoutOrgId({ + const organizerWithCredentials = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: { ...organizer, credentials }, }); diff --git a/packages/features/flags/config.ts b/packages/features/flags/config.ts index bf4145e927..5b83cfd34c 100644 --- a/packages/features/flags/config.ts +++ b/packages/features/flags/config.ts @@ -16,6 +16,6 @@ export type AppFlags = { "disable-signup": boolean; attributes: boolean; "organizer-request-email-v2": boolean; - "domain-wide-delegation": boolean; + "delegation-credential": boolean; "salesforce-crm-tasker": boolean; }; diff --git a/packages/features/flags/hooks/index.ts b/packages/features/flags/hooks/index.ts index ca2672d829..556363af24 100644 --- a/packages/features/flags/hooks/index.ts +++ b/packages/features/flags/hooks/index.ts @@ -15,7 +15,7 @@ const initialData: AppFlags = { "disable-signup": false, attributes: false, "organizer-request-email-v2": false, - "domain-wide-delegation": false, + "delegation-credential": false, "salesforce-crm-tasker": false, }; diff --git a/packages/lib/CalendarAppError.ts b/packages/lib/CalendarAppError.ts index 9dc0f3e885..5f815e4804 100644 --- a/packages/lib/CalendarAppError.ts +++ b/packages/lib/CalendarAppError.ts @@ -5,37 +5,37 @@ export class CalendarAppError extends Error { } } -export class CalendarAppDomainWideDelegationError extends CalendarAppError { +export class CalendarAppDelegationCredentialError extends CalendarAppError { constructor(message: string) { super(message); - this.name = "CalendarAppDomainWideDelegationError"; + this.name = "CalendarAppDelegationCredentialError"; } } -export class CalendarAppDomainWideDelegationConfigurationError extends CalendarAppDomainWideDelegationError { +export class CalendarAppDelegationCredentialConfigurationError extends CalendarAppDelegationCredentialError { constructor(message: string) { super(message); - this.name = "CalendarAppDomainWideDelegationConfigurationError"; + this.name = "CalendarAppDelegationCredentialConfigurationError"; } } -export class CalendarAppDomainWideDelegationInvalidGrantError extends CalendarAppDomainWideDelegationError { +export class CalendarAppDelegationCredentialInvalidGrantError extends CalendarAppDelegationCredentialError { constructor(message: string) { super(message); - this.name = "CalendarAppDomainWideDelegationInvalidGrantError"; + this.name = "CalendarAppDelegationCredentialInvalidGrantError"; } } -export class CalendarAppDomainWideDelegationClientIdNotAuthorizedError extends CalendarAppDomainWideDelegationConfigurationError { +export class CalendarAppDelegationCredentialClientIdNotAuthorizedError extends CalendarAppDelegationCredentialConfigurationError { constructor(message: string) { super(message); - this.name = "CalendarAppDomainWideDelegationClientIdNotAuthorizedError"; + this.name = "CalendarAppDelegationCredentialClientIdNotAuthorizedError"; } } -export class CalendarAppDomainWideDelegationNotSetupError extends CalendarAppDomainWideDelegationConfigurationError { +export class CalendarAppDelegationCredentialNotSetupError extends CalendarAppDelegationCredentialConfigurationError { constructor(message: string) { super(message); - this.name = "CalendarAppDomainWideDelegationNotSetupError"; + this.name = "CalendarAppDelegationCredentialNotSetupError"; } } diff --git a/packages/lib/CalendarManager.ts b/packages/lib/CalendarManager.ts index 7fad9713b4..8074c260c3 100644 --- a/packages/lib/CalendarManager.ts +++ b/packages/lib/CalendarManager.ts @@ -5,8 +5,8 @@ import { getCalendar } from "@calcom/app-store/_utils/getCalendar"; import getApps from "@calcom/app-store/utils"; import dayjs from "@calcom/dayjs"; import { getUid } from "@calcom/lib/CalEventParser"; -import { CalendarAppDomainWideDelegationError } from "@calcom/lib/CalendarAppError"; -import { buildNonDwdCredentials } from "@calcom/lib/domainWideDelegation/clientAndServer"; +import { CalendarAppDelegationCredentialError } from "@calcom/lib/CalendarAppError"; +import { buildNonDelegationCredentials } from "@calcom/lib/delegationCredential/clientAndServer"; import logger from "@calcom/lib/logger"; import { getPiiFreeCalendarEvent, getPiiFreeCredential } from "@calcom/lib/piiFreeData"; import { safeStringify } from "@calcom/lib/safeStringify"; @@ -40,8 +40,8 @@ export const getCalendarCredentials = (credentials: Array { - return getCalendarCredentials(buildNonDwdCredentials(credentials)); +export const getCalendarCredentialsWithoutDelegation = (credentials: CredentialPayload[]) => { + return getCalendarCredentials(buildNonDelegationCredentials(credentials)); }; export const getConnectedCalendars = async ( @@ -57,12 +57,12 @@ export const getConnectedCalendars = async ( const calendar = await item.calendar; // Don't leak credentials to the client const credentialId = credential.id; - const domainWideDelegationCredentialId = credential.delegatedToId ?? null; + const delegationCredentialId = credential.delegatedToId ?? null; if (!calendar) { return { integration, credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, }; } const cals = await calendar.listCalendars(); @@ -75,7 +75,7 @@ export const getConnectedCalendars = async ( primary: cal.primary || null, isSelected: selectedCalendars.some((selected) => selected.externalId === cal.externalId), credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, }; }), ["primary"] @@ -100,7 +100,7 @@ export const getConnectedCalendars = async ( return { integration: cleanIntegrationKeys(integration), credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, primary, calendars, }; @@ -114,7 +114,7 @@ export const getConnectedCalendars = async ( } } - if (error instanceof CalendarAppDomainWideDelegationError) { + if (error instanceof CalendarAppDelegationCredentialError) { errorMessage = error.message; } @@ -123,7 +123,7 @@ export const getConnectedCalendars = async ( return { integration: cleanIntegrationKeys(item.integration), credentialId: item.credential.id, - domainWideDelegationCredentialId: item.credential.delegatedToId, + delegationCredentialId: item.credential.delegatedToId, error: { message: errorMessage, }, @@ -153,7 +153,7 @@ const cleanIntegrationKeys = ( export const getBusyCalendarTimes = async ( /** - * withCredentials can possibly have a duplicate credential in case DWD is enabled. + * withCredentials can possibly have a duplicate credential in case DelegationCredential is enabled. * There is no way to deduplicate that at the moment because a `credential` doesn't directly know for which email it is, */ withCredentials: CredentialForCalendarService[], @@ -209,14 +209,16 @@ export const createEvent = async ( calEvent.additionalNotes = "Notes have been hidden by the organizer"; // TODO: i18n this string? } - const externalCalendarIdWhenDwdCredentialIsChosen = credential.delegatedToId ? externalId : undefined; + const externalCalendarIdWhenDelegationCredentialIsChosen = credential.delegatedToId + ? externalId + : undefined; // TODO: Surface success/error messages coming from apps to improve end user visibility const creationResult = calendar ? await calendar - // Ideally we should pass externalId always, but let's start with DWD case first as in that case, CalendarService need to handle a special case for DWD to determine the selectedCalendar. + // Ideally we should pass externalId always, but let's start with DelegationCredential case first as in that case, CalendarService need to handle a special case for DelegationCredential to determine the selectedCalendar. // Such logic shouldn't exist in CalendarService as it would be same for all calendar apps. - .createEvent(calEvent, credential.id, externalCalendarIdWhenDwdCredentialIsChosen) + .createEvent(calEvent, credential.id, externalCalendarIdWhenDelegationCredentialIsChosen) .catch(async (error: { code: number; calError: string }) => { success = false; /** diff --git a/packages/lib/EventManager.ts b/packages/lib/EventManager.ts index b817f79be4..927f08df59 100644 --- a/packages/lib/EventManager.ts +++ b/packages/lib/EventManager.ts @@ -82,13 +82,13 @@ const getCredential = ({ allCredentials, }: { id: { - domainWideDelegationCredentialId: string | null; + delegationCredentialId: string | null; credentialId: number | null; }; allCredentials: CredentialForCalendarService[]; }) => { - return id.domainWideDelegationCredentialId - ? allCredentials.find((c) => c.delegatedToId === id.domainWideDelegationCredentialId) + return id.delegationCredentialId + ? allCredentials.find((c) => c.delegatedToId === id.delegationCredentialId) : allCredentials.find((c) => c.id === id.credentialId); }; @@ -108,12 +108,12 @@ export const processLocation = (event: CalendarEvent): CalendarEvent => { }; /** - * Ensures invalid non-dwd credentialId isn't returned + * Ensures invalid non-delegationCredentialId isn't returned */ function getCredentialPayload(result: EventResult>) { return { credentialId: result?.credentialId && result.credentialId > 0 ? result.credentialId : undefined, - domainWideDelegationCredentialId: result?.delegatedToId || undefined, + delegationCredentialId: result?.delegatedToId || undefined, }; } @@ -155,7 +155,7 @@ export default class EventManager { .sort(latestCredentialFirst) // TODO: Change it to delegatedCredentialFirst in a followup PR. // We are keeping delegated credentials at the end so that there is no impact on existing users connections as we still use their existing credentials - // Soon after DWD is released and stable, we switch it. Could be an env variable also to toggle this. + // Soon after DelegationCredential is released and stable, we switch it. Could be an env variable also to toggle this. .sort(delegatedCredentialLast); this.videoCredentials = appCredentials @@ -364,7 +364,7 @@ export default class EventManager { credentialId, this.calendarCredentials, credentialType, - reference.domainWideDelegationCredentialId + reference.delegationCredentialId ); if (calendarCredential) { @@ -396,10 +396,10 @@ export default class EventManager { credentialId: number | null | undefined, credentials: CredentialForCalendarService[], type: string, - domainWideDelegationCredentialId?: string | null + delegationCredentialId?: string | null ) { - if (domainWideDelegationCredentialId) { - return this.calendarCredentials.find((cred) => cred.delegatedToId === domainWideDelegationCredentialId); + if (delegationCredentialId) { + return this.calendarCredentials.find((cred) => cred.delegatedToId === delegationCredentialId); } const credential = credentials.find((cred) => cred.id === credentialId); if (credential) { @@ -699,11 +699,11 @@ export default class EventManager { for (const destination of destinationCalendars) { if (eventCreated) break; log.silly("Creating Calendar event", JSON.stringify({ destination })); - if (destination.credentialId || destination.domainWideDelegationCredentialId) { + if (destination.credentialId || destination.delegationCredentialId) { let credential = getCredential({ id: { credentialId: destination.credentialId, - domainWideDelegationCredentialId: destination.domainWideDelegationCredentialId, + delegationCredentialId: destination.delegationCredentialId, }, allCredentials: this.calendarCredentials, }); @@ -728,14 +728,16 @@ export default class EventManager { delegatedTo: credentialFromDB.delegatedTo, }; } - } else if (destination.domainWideDelegationCredentialId) { - log.warn("DWD: DWD seems to be disabled, falling back to first non-dwd credential"); - // In case DWD is disabled, we land here where the destination calendar is connected to a DWD credential, but the credential isn't available(because DWD is disabled) - // In this case, we fallback to the first non-dwd credential. That would be there for all existing users before DWD was enabled - const firstNonDwdCalendarCredential = this.calendarCredentials.find( + } else if (destination.delegationCredentialId) { + log.warn( + "DelegationCredential: DelegationCredential seems to be disabled, falling back to first non-delegationCredential" + ); + // In case DelegationCredential is disabled, we land here where the destination calendar is connected to a Delegation credential, but the credential isn't available(because DelegationCredential is disabled) + // In this case, we fallback to the first non-delegationCredential. That would be there for all existing users before DelegationCredential was enabled + const firstNonDelegatedCalendarCredential = this.calendarCredentials.find( (cred) => !cred.type.endsWith("other_calendar") && !cred.delegatedToId ); - credential = firstNonDwdCalendarCredential; + credential = firstNonDelegatedCalendarCredential; } } if (credential) { diff --git a/packages/lib/apps/getEnabledAppsFromCredentials.ts b/packages/lib/apps/getEnabledAppsFromCredentials.ts index a154c504f8..210398a1dc 100644 --- a/packages/lib/apps/getEnabledAppsFromCredentials.ts +++ b/packages/lib/apps/getEnabledAppsFromCredentials.ts @@ -4,7 +4,7 @@ import type { CredentialDataWithTeamName } from "@calcom/app-store/utils"; import getApps from "@calcom/app-store/utils"; import { prisma } from "@calcom/prisma"; -import { isDwdCredential } from "../domainWideDelegation/clientAndServer"; +import { isDelegationCredential } from "../delegationCredential/clientAndServer"; type EnabledApp = ReturnType[number] & { enabled: boolean }; @@ -32,8 +32,8 @@ const getEnabledAppsFromCredentials = async ( }, } satisfies Prisma.AppWhereInput; - const dwdCredentialsWithAppId = credentials - .filter((credential) => isDwdCredential({ credentialId: credential.id })) + const delegationCredentialsWithAppId = credentials + .filter((credential) => isDelegationCredential({ credentialId: credential.id })) .filter((credential): credential is typeof credential & { appId: string } => credential.appId !== null); if (filterOnCredentials) { @@ -59,17 +59,17 @@ const getEnabledAppsFromCredentials = async ( select: { slug: true, enabled: true }, }); - const dwdSupportedEnabledApps = await prisma.app.findMany({ + const delegationCredentialSupportedEnabledApps = await prisma.app.findMany({ where: { enabled: true, slug: { - in: dwdCredentialsWithAppId.map((credential) => credential.appId), + in: delegationCredentialsWithAppId.map((credential) => credential.appId), }, }, select: { slug: true, enabled: true }, }); - enabledApps = [...enabledApps, ...dwdSupportedEnabledApps]; + enabledApps = [...enabledApps, ...delegationCredentialSupportedEnabledApps]; const apps = getApps(credentials, filterOnCredentials); const filteredApps = apps.reduce((reducedArray, app) => { diff --git a/packages/lib/bookings/findQualifiedHostsWithDwdCredentials.test.ts b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.test.ts similarity index 95% rename from packages/lib/bookings/findQualifiedHostsWithDwdCredentials.test.ts rename to packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.test.ts index 4c8e47bd37..8d8e16d6a2 100644 --- a/packages/lib/bookings/findQualifiedHostsWithDwdCredentials.test.ts +++ b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.test.ts @@ -6,7 +6,7 @@ import type { Mock } from "vitest"; import { SchedulingType } from "@calcom/prisma/enums"; import { filterHostsByLeadThreshold } from "./filterHostsByLeadThreshold"; -import { findQualifiedHostsWithDwdCredentials } from "./findQualifiedHostsWithDwdCredentials"; +import { findQualifiedHostsWithDelegationCredentials } from "./findQualifiedHostsWithDelegationCredentials"; import * as getRoutedUsers from "./getRoutedUsers"; // Mock the filterHostsByLeadThreshold function @@ -21,7 +21,7 @@ afterEach(() => { (filterHostsByLeadThreshold as Mock).mockClear(); }); -describe("findQualifiedHostsWithDwdCredentials", async () => { +describe("findQualifiedHostsWithDelegationCredentials", async () => { it("should return qualified hosts based on mock of filterHostsByLeadThreshold", async () => { const hosts = [ { @@ -88,7 +88,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: null, @@ -139,7 +139,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: null, @@ -225,7 +225,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: null, @@ -313,7 +313,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { }; // Call the function under test - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [1], rescheduleUid: null, @@ -390,7 +390,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { }; prismaMock.booking.findFirst.mockResolvedValue({ userId: 2 }); - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [], rescheduleUid: "recheduleUid", @@ -469,7 +469,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { .mockImplementation(async () => [hosts[0]]); // Call the function under test - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [0, 1, 2], rescheduleUid: null, @@ -562,7 +562,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { ]); // Call the function under test - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [2, 3], rescheduleUid: null, @@ -661,7 +661,7 @@ describe("findQualifiedHostsWithDwdCredentials", async () => { (filterHostsByLeadThreshold as Mock).mockResolvedValue(rrHostsAfterFairness); // Call the function under test - const result = await findQualifiedHostsWithDwdCredentials({ + const result = await findQualifiedHostsWithDelegationCredentials({ eventType, routedTeamMemberIds: [2, 3], rescheduleUid: null, diff --git a/packages/lib/bookings/findQualifiedHostsWithDwdCredentials.ts b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.ts similarity index 97% rename from packages/lib/bookings/findQualifiedHostsWithDwdCredentials.ts rename to packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.ts index a282c4e70f..91ee3ef0e2 100644 --- a/packages/lib/bookings/findQualifiedHostsWithDwdCredentials.ts +++ b/packages/lib/bookings/findQualifiedHostsWithDelegationCredentials.ts @@ -1,6 +1,6 @@ import { findMatchingHostsWithEventSegment, - getNormalizedHostsWithDwdCredentials, + getNormalizedHostsWithDelegationCredentials, } from "@calcom/lib/bookings/getRoutedUsers"; import type { EventType } from "@calcom/lib/bookings/getRoutedUsers"; import type { SelectedCalendar } from "@calcom/prisma/client"; @@ -46,7 +46,7 @@ const isFixedHost = (host: T): host is T & { isF return host.isFixed; }; -export const findQualifiedHostsWithDwdCredentials = async < +export const findQualifiedHostsWithDelegationCredentials = async < T extends { email: string; id: number; @@ -97,11 +97,10 @@ export const findQualifiedHostsWithDwdCredentials = async < user: Omit & { credentials: CredentialForCalendarService[] }; }[]; }> => { - const { hosts: normalizedHosts, fallbackHosts: fallbackUsers } = await getNormalizedHostsWithDwdCredentials( - { + const { hosts: normalizedHosts, fallbackHosts: fallbackUsers } = + await getNormalizedHostsWithDelegationCredentials({ eventType, - } - ); + }); // not a team event type, or some other reason - segment matching isn't necessary. if (!normalizedHosts) { const fixedHosts = fallbackUsers.filter(isFixedHost); diff --git a/packages/lib/bookings/getRoutedUsers.ts b/packages/lib/bookings/getRoutedUsers.ts index bfe1cf3172..330c1bd437 100644 --- a/packages/lib/bookings/getRoutedUsers.ts +++ b/packages/lib/bookings/getRoutedUsers.ts @@ -5,7 +5,7 @@ import { safeStringify } from "@calcom/lib/safeStringify"; import { SchedulingType } from "@calcom/prisma/enums"; import type { CredentialPayload } from "@calcom/types/Credential"; -import { enrichHostsWithDwdCredentials } from "../domainWideDelegation/server"; +import { enrichHostsWithDelegationCredentials } from "../delegationCredential/server"; import getOrgIdFromMemberOrTeamId from "../getOrgIdFromMemberOrTeamId"; const log = logger.getSubLogger({ prefix: ["[getRoutedUsers]"] }); @@ -118,7 +118,7 @@ export function getNormalizedHosts >({ @@ -132,28 +132,28 @@ export async function getNormalizedHostsWithDwdCredentials< }; }) { if (eventType.hosts?.length && eventType.schedulingType) { - const hostsWithoutDwd = eventType.hosts.map((host) => ({ + const hostsWithoutDelegationCredential = eventType.hosts.map((host) => ({ isFixed: host.isFixed, user: host.user, priority: host.priority, weight: host.weight, createdAt: host.createdAt, })); - const firstHost = hostsWithoutDwd[0]; + const firstHost = hostsWithoutDelegationCredential[0]; const firstUserOrgId = await getOrgIdFromMemberOrTeamId({ memberId: firstHost?.user?.id ?? null, teamId: eventType.teamId, }); - const hostsEnrichedWithDwd = await enrichHostsWithDwdCredentials({ + const hostsEnrichedWithDelegationCredential = await enrichHostsWithDelegationCredentials({ orgId: firstUserOrgId ?? null, - hosts: hostsWithoutDwd ?? null, + hosts: hostsWithoutDelegationCredential ?? null, }); return { - hosts: hostsEnrichedWithDwd, + hosts: hostsEnrichedWithDelegationCredential, fallbackHosts: null, }; } else { - const hostsWithoutDwd = eventType.users.map((user) => { + const hostsWithoutDelegationCredential = eventType.users.map((user) => { return { isFixed: !eventType.schedulingType || eventType.schedulingType === SchedulingType.COLLECTIVE, email: user.email, @@ -161,18 +161,18 @@ export async function getNormalizedHostsWithDwdCredentials< createdAt: null, }; }); - const firstHost = hostsWithoutDwd[0]; + const firstHost = hostsWithoutDelegationCredential[0]; const firstUserOrgId = await getOrgIdFromMemberOrTeamId({ memberId: firstHost?.user?.id ?? null, teamId: eventType.teamId, }); - const hostsEnrichedWithDwd = await enrichHostsWithDwdCredentials({ + const hostsEnrichedWithDelegationCredential = await enrichHostsWithDelegationCredentials({ orgId: firstUserOrgId ?? null, - hosts: hostsWithoutDwd ?? null, + hosts: hostsWithoutDelegationCredential ?? null, }); return { hosts: null, - fallbackHosts: hostsEnrichedWithDwd, + fallbackHosts: hostsEnrichedWithDelegationCredential, }; } } diff --git a/packages/lib/buildCalEventFromBooking.ts b/packages/lib/buildCalEventFromBooking.ts index 247fd6594e..23866b604a 100644 --- a/packages/lib/buildCalEventFromBooking.ts +++ b/packages/lib/buildCalEventFromBooking.ts @@ -13,7 +13,7 @@ type DestinationCalendar = { userId: number | null; eventTypeId: number | null; credentialId: number | null; - domainWideDelegationCredentialId: string | null; + delegationCredentialId: string | null; } | null; type Attendee = { diff --git a/packages/lib/delegationCredential/clientAndServer.ts b/packages/lib/delegationCredential/clientAndServer.ts new file mode 100644 index 0000000000..261eefe1e1 --- /dev/null +++ b/packages/lib/delegationCredential/clientAndServer.ts @@ -0,0 +1,27 @@ +export function isDelegationCredential({ credentialId }: { credentialId: number | null | undefined }) { + // Though it is set as -1 right now, but we might want to set it to some other negative value. + return typeof credentialId === "number" && credentialId < 0; +} + +export const buildNonDelegationCredential = | null>(credential: T) => { + type WithDelegationCredential = T extends null + ? null + : T & { + delegatedTo: null; + delegatedToId: null; + }; + + if (!credential) return null as WithDelegationCredential; + return { + ...credential, + delegatedTo: null, + delegatedToId: null, + } as WithDelegationCredential; +}; + +export const buildNonDelegationCredentials = >(credentials: T[]) => { + return credentials.map(buildNonDelegationCredential).filter((credential) => !!credential) as (T & { + delegatedTo: null; + delegatedToId: null; + })[]; +}; diff --git a/packages/lib/domainWideDelegation/server.test.ts b/packages/lib/delegationCredential/server.test.ts similarity index 60% rename from packages/lib/domainWideDelegation/server.test.ts rename to packages/lib/delegationCredential/server.test.ts index 74fa827d0d..4884110661 100644 --- a/packages/lib/domainWideDelegation/server.test.ts +++ b/packages/lib/delegationCredential/server.test.ts @@ -4,20 +4,20 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; import { metadata as googleCalendarMetadata } from "@calcom/app-store/googlecalendar/_metadata"; import { metadata as googleMeetMetadata } from "@calcom/app-store/googlevideo/_metadata"; -import type { ServiceAccountKey } from "@calcom/lib/server/repository/domainWideDelegation"; -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; +import type { ServiceAccountKey } from "@calcom/lib/server/repository/delegationCredential"; +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; import { OrganizationRepository } from "@calcom/lib/server/repository/organization"; import { SMSLockState } from "@calcom/prisma/enums"; import type { CredentialForCalendarService, CredentialPayload } from "@calcom/types/Credential"; import { - getAllDwdCredentialsForUser, + getAllDelegationCredentialsForUser, buildAllCredentials, - getDwdOrRegularCredential, - enrichUsersWithDwdCredentials, - enrichHostsWithDwdCredentials, - enrichUserWithDwdCredentialsWithoutOrgId, - enrichUserWithDwdConferencingCredentialsWithoutOrgId, + getDelegationCredentialOrRegularCredential, + enrichUsersWithDelegationCredentials, + enrichHostsWithDelegationCredentials, + enrichUserWithDelegationCredentialsWithoutOrgId, + enrichUserWithDelegationConferencingCredentialsWithoutOrgId, } from "./server"; // Mock OrganizationRepository @@ -27,9 +27,9 @@ vi.mock("@calcom/lib/server/repository/organization", () => ({ }, })); -// Mock DomainWideDelegationRepository -vi.mock("@calcom/lib/server/repository/domainWideDelegation", () => ({ - DomainWideDelegationRepository: { +// Mock DelegationCredentialRepository +vi.mock("@calcom/lib/server/repository/delegationCredential", () => ({ + DelegationCredentialRepository: { findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey: vi.fn(), findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey: vi.fn(), }, @@ -51,8 +51,8 @@ const mockWorkspacePlatform = { slug: "google", }; -const mockDwd = { - id: "dwd-1", +const mockDelegationCredential = { + id: "delegationCredential-1", enabled: true, domain: "example.com", organizationId: 1, @@ -106,11 +106,11 @@ const mockOrganization = { }; // Credential Builders -const buildDwdCredential = (overrides = {}) => ({ +const buildDelegationCredential = (overrides = {}) => ({ type: "google_calendar", appId: "google-calendar", id: -1, - delegatedToId: mockDwd.id, + delegatedToId: mockDelegationCredential.id, userId: mockUser.id, user: { email: mockUser.email }, key: { access_token: "NOOP_UNUSED_DELEGATION_TOKEN" }, @@ -123,16 +123,16 @@ const buildDwdCredential = (overrides = {}) => ({ ...overrides, }); -const buildDwdGoogleCalendarCredential = (overrides = {}) => ({ - ...buildDwdCredential({ +const buildGoogleCalendarDelegationCredential = (overrides = {}) => ({ + ...buildDelegationCredential({ type: googleCalendarMetadata.type, appId: googleCalendarMetadata.slug, }), ...overrides, }); -const buildDwdGoogleMeetCredential = (overrides = {}) => ({ - ...buildDwdCredential({ +const buildDelegationCredentialGoogleMeetCredential = (overrides = {}) => ({ + ...buildDelegationCredential({ type: googleMeetMetadata.type, appId: googleMeetMetadata.slug, }), @@ -167,13 +167,13 @@ const buildMockWorkspacePlatform = (overrides: Partial = {}) => ({ - ...mockDwd, +const buildMockDelegationCredential = (overrides: Partial = {}) => ({ + ...mockDelegationCredential, workspacePlatform: buildMockWorkspacePlatform(overrides.workspacePlatform || {}), ...overrides, }); -describe("getAllDwdCredentialsForUser", () => { +describe("getAllDelegationCredentialsForUser", () => { setupAndTeardown(); beforeEach(() => { @@ -181,138 +181,141 @@ describe("getAllDwdCredentialsForUser", () => { vi.mocked(OrganizationRepository.findByMemberEmail).mockResolvedValue(mockOrganization); }); - it("should return empty array when no DWD found", async () => { + it("should return empty array when no DelegationCredential found", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey ).mockResolvedValue(null); - const result = await getAllDwdCredentialsForUser({ user: mockUser }); + const result = await getAllDelegationCredentialsForUser({ user: mockUser }); expect(result).toEqual([]); }); - it("should return empty array when DWD is disabled", async () => { + it("should return empty array when DelegationCredential is disabled", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd({ enabled: false })); - const result = await getAllDwdCredentialsForUser({ user: mockUser }); + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential({ enabled: false })); + const result = await getAllDelegationCredentialsForUser({ user: mockUser }); expect(result).toEqual([]); }); - it("should return credentials for enabled Google DWD", async () => { + it("should return credentials for enabled Google DelegationCredential", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd()); - const result = await getAllDwdCredentialsForUser({ user: mockUser }); + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential()); + const result = await getAllDelegationCredentialsForUser({ user: mockUser }); expect(result).toHaveLength(2); - expect(result).toEqual([buildDwdGoogleCalendarCredential(), buildDwdGoogleMeetCredential()]); + expect(result).toEqual([ + buildGoogleCalendarDelegationCredential(), + buildDelegationCredentialGoogleMeetCredential(), + ]); }); it("should return empty array for non-Google platforms(as they are not supported yet)", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey ).mockResolvedValue( - buildMockDwd({ + buildMockDelegationCredential({ workspacePlatform: { name: "Microsoft", slug: "microsoft", }, }) ); - const result = await getAllDwdCredentialsForUser({ user: mockUser }); + const result = await getAllDelegationCredentialsForUser({ user: mockUser }); expect(result).toEqual([]); }); }); describe("buildAllCredentials", () => { - const mockDwdGoogleCalendarCred = buildDwdGoogleCalendarCredential(); + const mockDelegationCredentialGoogleCalendarCred = buildGoogleCalendarDelegationCredential(); const mockRegularGoogleCalendarCred = buildRegularGoogleCalendarCredential(); - it("should combine DWD and regular credentials", () => { + it("should combine DelegationCredential and regular credentials", () => { const result = buildAllCredentials({ - dwdCredentials: [mockDwdGoogleCalendarCred], + delegationCredentials: [mockDelegationCredentialGoogleCalendarCred], existingCredentials: [mockRegularGoogleCalendarCred], }); expect(result).toHaveLength(2); - expect(result).toContainEqual(mockDwdGoogleCalendarCred); + expect(result).toContainEqual(mockDelegationCredentialGoogleCalendarCred); expect(result).toContainEqual(mockRegularGoogleCalendarCred); }); - it("should deduplicate DWD credentials with same delegatedToId and appId", () => { - const duplicateDwdCredential = buildDwdGoogleCalendarCredential(); + it("should deduplicate DelegationCredential credentials with same delegatedToId and appId", () => { + const duplicateDelegationCredential = buildGoogleCalendarDelegationCredential(); const result = buildAllCredentials({ - dwdCredentials: [mockDwdGoogleCalendarCred, duplicateDwdCredential], + delegationCredentials: [mockDelegationCredentialGoogleCalendarCred, duplicateDelegationCredential], existingCredentials: [], }); expect(result).toHaveLength(1); - expect(result[0]).toEqual(mockDwdGoogleCalendarCred); + expect(result[0]).toEqual(mockDelegationCredentialGoogleCalendarCred); }); - it("should keep DWD credentials with same delegatedToId but different appId", () => { - const differentAppDwdCredential = buildDwdGoogleMeetCredential(); + it("should keep DelegationCredential credentials with same delegatedToId but different appId", () => { + const differentAppDelegationCredential = buildDelegationCredentialGoogleMeetCredential(); const result = buildAllCredentials({ - dwdCredentials: [mockDwdGoogleCalendarCred, differentAppDwdCredential], + delegationCredentials: [mockDelegationCredentialGoogleCalendarCred, differentAppDelegationCredential], existingCredentials: [], }); expect(result).toHaveLength(2); - expect(result).toContainEqual(mockDwdGoogleCalendarCred); - expect(result).toContainEqual(differentAppDwdCredential); + expect(result).toContainEqual(mockDelegationCredentialGoogleCalendarCred); + expect(result).toContainEqual(differentAppDelegationCredential); }); - it("should filter out DWD credentials from existingCredentials", () => { - const dwdCalendarCredential = buildRegularGoogleCalendarCredential({ id: -2 }); + it("should filter out DelegationCredential credentials from existingCredentials", () => { + const delegatedCalendarCredential = buildRegularGoogleCalendarCredential({ id: -2 }); const result = buildAllCredentials({ - dwdCredentials: [mockDwdGoogleCalendarCred], - existingCredentials: [mockRegularGoogleCalendarCred, dwdCalendarCredential], + delegationCredentials: [mockDelegationCredentialGoogleCalendarCred], + existingCredentials: [mockRegularGoogleCalendarCred, delegatedCalendarCredential], }); expect(result).toHaveLength(2); - expect(result).toContainEqual(mockDwdGoogleCalendarCred); + expect(result).toContainEqual(mockDelegationCredentialGoogleCalendarCred); expect(result).toContainEqual(mockRegularGoogleCalendarCred); }); }); -describe("getDwdOrRegularCredential", () => { - const mockDwdGoogleCalendarCred = buildDwdGoogleCalendarCredential(); +describe("getDelegationCredentialOrRegularCredential", () => { + const mockDelegationCredentialGoogleCalendarCred = buildGoogleCalendarDelegationCredential(); const mockRegularGoogleCalendarCred = buildRegularGoogleCalendarCredential(); - const credentials = [mockDwdGoogleCalendarCred, mockRegularGoogleCalendarCred]; + const credentials = [mockDelegationCredentialGoogleCalendarCred, mockRegularGoogleCalendarCred]; - it("should find DWD credential by dwdId", () => { - const result = getDwdOrRegularCredential({ + it("should find Delegation credential by delegationCredentialId", () => { + const result = getDelegationCredentialOrRegularCredential({ credentials, - id: { credentialId: null, dwdId: mockDwd.id }, + id: { credentialId: null, delegationCredentialId: mockDelegationCredential.id }, }); - expect(result).toEqual(mockDwdGoogleCalendarCred); + expect(result).toEqual(mockDelegationCredentialGoogleCalendarCred); }); it("should find regular credential by credentialId", () => { - const result = getDwdOrRegularCredential({ + const result = getDelegationCredentialOrRegularCredential({ credentials, - id: { credentialId: mockRegularGoogleCalendarCred.id, dwdId: null }, + id: { credentialId: mockRegularGoogleCalendarCred.id, delegationCredentialId: null }, }); expect(result).toEqual(mockRegularGoogleCalendarCred); }); it("should return null when no matching credential found", () => { - const result = getDwdOrRegularCredential({ + const result = getDelegationCredentialOrRegularCredential({ credentials, - id: { credentialId: 999, dwdId: null }, + id: { credentialId: 999, delegationCredentialId: null }, }); expect(result).toBeNull(); }); it("should not match null to null for delegatedToId", () => { - const result = getDwdOrRegularCredential({ + const result = getDelegationCredentialOrRegularCredential({ credentials, - id: { credentialId: null, dwdId: null }, + id: { credentialId: null, delegationCredentialId: null }, }); expect(result).toBeNull(); }); }); -describe("enrichUsersWithDwdCredentials", () => { +describe("enrichUsersWithDelegationCredentials", () => { const mockUsers = [ { ...mockUser, @@ -330,7 +333,7 @@ describe("enrichUsersWithDwdCredentials", () => { }); it("should return users as is when orgId is null", async () => { - const result = await enrichUsersWithDwdCredentials({ + const result = await enrichUsersWithDelegationCredentials({ orgId: null, users: mockUsers, }); @@ -347,19 +350,19 @@ describe("enrichUsersWithDwdCredentials", () => { ); }); - it("should enrich users with DWD credentials when available", async () => { + it("should enrich users with DelegationCredential credentials when available", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd()); + DelegationCredentialRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential()); - const result = await enrichUsersWithDwdCredentials({ + const result = await enrichUsersWithDelegationCredentials({ orgId: 1, users: mockUsers, }); expect(result).toHaveLength(2); result.forEach((enrichedUser) => { - expect(enrichedUser.credentials).toHaveLength(3); // 1 regular + 2 DWD (calendar + meet) + expect(enrichedUser.credentials).toHaveLength(3); // 1 regular + 2 DelegationCredential (calendar + meet) expect(enrichedUser.credentials).toContainEqual( expect.objectContaining({ type: googleCalendarMetadata.type, @@ -375,12 +378,12 @@ describe("enrichUsersWithDwdCredentials", () => { }); }); - it("should not add DWD credentials when DWD is disabled", async () => { + it("should not add DelegationCredential credentials when DelegationCredential is disabled", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd({ enabled: false })); + DelegationCredentialRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential({ enabled: false })); - const result = await enrichUsersWithDwdCredentials({ + const result = await enrichUsersWithDelegationCredentials({ orgId: 1, users: mockUsers, }); @@ -398,7 +401,7 @@ describe("enrichUsersWithDwdCredentials", () => { }); }); -describe("enrichHostsWithDwdCredentials", () => { +describe("enrichHostsWithDelegationCredentials", () => { const mockHosts = [ { user: { @@ -422,7 +425,7 @@ describe("enrichHostsWithDwdCredentials", () => { }); it("should return hosts as is when orgId is null", async () => { - const result = await enrichHostsWithDwdCredentials({ + const result = await enrichHostsWithDelegationCredentials({ orgId: null, hosts: mockHosts, }); @@ -442,12 +445,12 @@ describe("enrichHostsWithDwdCredentials", () => { ); }); - it("should enrich hosts with DWD credentials when available", async () => { + it("should enrich hosts with DelegationCredential credentials when available", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd()); + DelegationCredentialRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential()); - const result = await enrichHostsWithDwdCredentials({ + const result = await enrichHostsWithDelegationCredentials({ orgId: 1, hosts: mockHosts, }); @@ -455,7 +458,7 @@ describe("enrichHostsWithDwdCredentials", () => { expect(result).toHaveLength(2); result.forEach((enrichedHost) => { expect(enrichedHost.metadata).toBeDefined(); // Preserve non-user data - expect(enrichedHost.user.credentials).toHaveLength(3); // 1 regular + 2 DWD (calendar + meet) + expect(enrichedHost.user.credentials).toHaveLength(3); // 1 regular + 2 DelegationCredential (calendar + meet) expect(enrichedHost.user.credentials).toContainEqual( expect.objectContaining({ type: googleCalendarMetadata.type, @@ -471,12 +474,12 @@ describe("enrichHostsWithDwdCredentials", () => { }); }); - it("should not add DWD credentials when DWD is disabled", async () => { + it("should not add DelegationCredential credentials when DelegationCredential is disabled", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd({ enabled: false })); + DelegationCredentialRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential({ enabled: false })); - const result = await enrichHostsWithDwdCredentials({ + const result = await enrichHostsWithDelegationCredentials({ orgId: 1, hosts: mockHosts, }); @@ -497,7 +500,7 @@ describe("enrichHostsWithDwdCredentials", () => { }); }); -describe("enrichUserWithDwdCredentialsWithoutOrgId", () => { +describe("enrichUserWithDelegationCredentialsWithoutOrgId", () => { const mockUserWithCredentials = { ...mockUser, credentials: [buildRegularGoogleCalendarCredential()], @@ -507,16 +510,16 @@ describe("enrichUserWithDwdCredentialsWithoutOrgId", () => { vi.clearAllMocks(); }); - it("should enrich user with DWD credentials when available", async () => { + it("should enrich user with DelegationCredential credentials when available", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd()); + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential()); - const result = await enrichUserWithDwdCredentialsWithoutOrgId({ + const result = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: mockUserWithCredentials, }); - expect(result.credentials).toHaveLength(3); // 1 regular + 2 DWD (calendar + meet) + expect(result.credentials).toHaveLength(3); // 1 regular + 2 DelegationCredential (calendar + meet) expect(result.credentials).toContainEqual( expect.objectContaining({ type: googleCalendarMetadata.type, @@ -531,12 +534,12 @@ describe("enrichUserWithDwdCredentialsWithoutOrgId", () => { ); }); - it("should not add DWD credentials when DWD is not found", async () => { + it("should not add DelegationCredential credentials when DelegationCredential is not found", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey ).mockResolvedValue(null); - const result = await enrichUserWithDwdCredentialsWithoutOrgId({ + const result = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: mockUserWithCredentials, }); @@ -550,12 +553,12 @@ describe("enrichUserWithDwdCredentialsWithoutOrgId", () => { }); }); - it("should not add DWD credentials when DWD is disabled", async () => { + it("should not add DelegationCredential credentials when DelegationCredential is disabled", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd({ enabled: false })); + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential({ enabled: false })); - const result = await enrichUserWithDwdCredentialsWithoutOrgId({ + const result = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: mockUserWithCredentials, }); @@ -570,7 +573,7 @@ describe("enrichUserWithDwdCredentialsWithoutOrgId", () => { }); }); -describe("enrichUserWithDwdConferencingCredentialsWithoutOrgId", () => { +describe("enrichUserWithDelegationConferencingCredentialsWithoutOrgId", () => { const mockUserWithCredentials = { ...mockUser, credentials: [buildRegularGoogleCalendarCredential()], @@ -582,10 +585,10 @@ describe("enrichUserWithDwdConferencingCredentialsWithoutOrgId", () => { it("should only return conferencing credentials", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey - ).mockResolvedValue(buildMockDwd()); + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + ).mockResolvedValue(buildMockDelegationCredential()); - const result = await enrichUserWithDwdConferencingCredentialsWithoutOrgId({ + const result = await enrichUserWithDelegationConferencingCredentialsWithoutOrgId({ user: mockUserWithCredentials, }); @@ -600,10 +603,10 @@ describe("enrichUserWithDwdConferencingCredentialsWithoutOrgId", () => { it("should return empty credentials array when no conferencing credentials found", async () => { vi.mocked( - DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey + DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey ).mockResolvedValue(null); - const result = await enrichUserWithDwdConferencingCredentialsWithoutOrgId({ + const result = await enrichUserWithDelegationConferencingCredentialsWithoutOrgId({ user: mockUserWithCredentials, }); diff --git a/packages/lib/domainWideDelegation/server.ts b/packages/lib/delegationCredential/server.ts similarity index 50% rename from packages/lib/domainWideDelegation/server.ts rename to packages/lib/delegationCredential/server.ts index c002d381b5..f21c8e4cc0 100644 --- a/packages/lib/domainWideDelegation/server.ts +++ b/packages/lib/delegationCredential/server.ts @@ -6,21 +6,21 @@ import { metadata as office365VideoMetaData } from "@calcom/app-store/office365v import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import { CredentialRepository } from "@calcom/lib/server/repository/credential"; -import type { ServiceAccountKey } from "@calcom/lib/server/repository/domainWideDelegation"; -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; +import type { ServiceAccountKey } from "@calcom/lib/server/repository/delegationCredential"; +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; import type { CredentialForCalendarService, CredentialPayload } from "@calcom/types/Credential"; -import { buildNonDwdCredentials, isDwdCredential } from "./clientAndServer"; +import { buildNonDelegationCredentials, isDelegationCredential } from "./clientAndServer"; -export { buildNonDwdCredentials, buildNonDwdCredential } from "./clientAndServer"; +export { buildNonDelegationCredentials, buildNonDelegationCredential } from "./clientAndServer"; const GOOGLE_WORKSPACE_SLUG = "google"; const OFFICE365_WORKSPACE_SLUG = "office365"; const WORKSPACE_PLATFORM_SLUGS = [GOOGLE_WORKSPACE_SLUG, OFFICE365_WORKSPACE_SLUG] as const; type WORKSPACE_PLATFORM_SLUGS_TYPE = (typeof WORKSPACE_PLATFORM_SLUGS)[number]; -const log = logger.getSubLogger({ prefix: ["lib/domainWideDelegation/server"] }); -interface DomainWideDelegation { +const log = logger.getSubLogger({ prefix: ["lib/delegationCredential/server"] }); +interface DelegationCredential { id: string; workspacePlatform: { slug: string; @@ -28,7 +28,7 @@ interface DomainWideDelegation { serviceAccountKey: ServiceAccountKey | null; } -interface DomainWideDelegationWithSensitiveServiceAccountKey extends DomainWideDelegation { +interface DelegationCredentialWithSensitiveServiceAccountKey extends DelegationCredential { serviceAccountKey: ServiceAccountKey; } @@ -41,7 +41,10 @@ const isValidWorkspaceSlug = (slug: string) => { return WORKSPACE_PLATFORM_SLUGS.includes(slug as unknown as WORKSPACE_PLATFORM_SLUGS_TYPE); }; -const getDwdAppMetadata = (slug: WORKSPACE_PLATFORM_SLUGS_TYPE, isConferencing?: boolean) => { +const getDelegationCredentialAppMetadata = ( + slug: WORKSPACE_PLATFORM_SLUGS_TYPE, + isConferencing?: boolean +) => { switch (slug) { case GOOGLE_WORKSPACE_SLUG: return isConferencing @@ -66,10 +69,16 @@ const _isConferencingCredential = (credential: CredentialPayload) => { ); }; -const _buildCommonUserCredential = ({ dwd, user }: { dwd: DomainWideDelegation; user: User }) => { +const _buildCommonUserCredential = ({ + delegationCredential, + user, +}: { + delegationCredential: DelegationCredential; + user: User; +}) => { return { id: -1, - delegatedToId: dwd.id, + delegatedToId: delegationCredential.id, userId: user.id, user: { email: user.email, @@ -80,159 +89,181 @@ const _buildCommonUserCredential = ({ dwd, user }: { dwd: DomainWideDelegation; invalid: false, teamId: null, team: null, - delegatedTo: dwd.serviceAccountKey + delegatedTo: delegationCredential.serviceAccountKey ? { - serviceAccountKey: dwd.serviceAccountKey, + serviceAccountKey: delegationCredential.serviceAccountKey, } : null, }; }; -const _buildDwdCalendarCredential = ({ dwd, user }: { dwd: DomainWideDelegation; user: User }) => { - log.debug("buildDomainWideDelegationCredential", safeStringify({ dwd, user })); +const _buildDelegatedCalendarCredential = ({ + delegationCredential, + user, +}: { + delegationCredential: DelegationCredential; + user: User; +}) => { + log.debug("buildDelegationCredential", safeStringify({ delegationCredential, user })); // TODO: Build for other platforms as well - if (!isValidWorkspaceSlug(dwd.workspacePlatform.slug)) { + if (!isValidWorkspaceSlug(delegationCredential.workspacePlatform.slug)) { log.warn( `Only ${WORKSPACE_PLATFORM_SLUGS.toString()} Platforms are supported here, skipping ${ - dwd.workspacePlatform.slug + delegationCredential.workspacePlatform.slug }` ); return null; } return { - ...getDwdAppMetadata(dwd.workspacePlatform.slug as unknown as WORKSPACE_PLATFORM_SLUGS_TYPE, false), - ..._buildCommonUserCredential({ dwd, user }), + ...getDelegationCredentialAppMetadata( + delegationCredential.workspacePlatform.slug as unknown as WORKSPACE_PLATFORM_SLUGS_TYPE, + false + ), + ..._buildCommonUserCredential({ delegationCredential, user }), }; }; -const _buildDwdCalendarCredentialWithServiceAccountKey = ({ - dwd, +const _buildDelegatedCalendarCredentialWithServiceAccountKey = ({ + delegationCredential, user, }: { - dwd: DomainWideDelegationWithSensitiveServiceAccountKey; + delegationCredential: DelegationCredentialWithSensitiveServiceAccountKey; user: User; }) => { - const credential = _buildDwdCalendarCredential({ dwd, user }); + const credential = _buildDelegatedCalendarCredential({ delegationCredential, user }); if (!credential) { return null; } return { ...credential, delegatedTo: { - serviceAccountKey: dwd.serviceAccountKey, + serviceAccountKey: delegationCredential.serviceAccountKey, }, }; }; -const _buildDwdConferencingCredential = ({ dwd, user }: { dwd: DomainWideDelegation; user: User }) => { +const _buildDelegatedConferencingCredential = ({ + delegationCredential, + user, +}: { + delegationCredential: DelegationCredential; + user: User; +}) => { // TODO: Build for other platforms as well - if (!isValidWorkspaceSlug(dwd.workspacePlatform.slug)) { + if (!isValidWorkspaceSlug(delegationCredential.workspacePlatform.slug)) { log.warn( `Only ${WORKSPACE_PLATFORM_SLUGS.toString()} Platforms are supported here, skipping ${ - dwd.workspacePlatform.slug + delegationCredential.workspacePlatform.slug }` ); return null; } return { - ...getDwdAppMetadata(dwd.workspacePlatform.slug as unknown as WORKSPACE_PLATFORM_SLUGS_TYPE, true), - ..._buildCommonUserCredential({ dwd, user }), + ...getDelegationCredentialAppMetadata( + delegationCredential.workspacePlatform.slug as unknown as WORKSPACE_PLATFORM_SLUGS_TYPE, + true + ), + ..._buildCommonUserCredential({ delegationCredential, user }), }; }; /** - * Gets calendar as well as conferencing credentials(stored in-memory) for the user from the corresponding enabled DomainWideDelegation. + * Gets calendar as well as conferencing credentials(stored in-memory) for the user from the corresponding enabled DelegationCredential. */ -export async function getAllDwdCredentialsForUser({ user }: { user: { email: string; id: number } }) { +export async function getAllDelegationCredentialsForUser({ user }: { user: { email: string; id: number } }) { log.debug("called with", safeStringify({ user })); // We access the repository without checking for feature flag here. - // In case we need to disable the effects of DWD on credential we need to toggle DWD off from organization settings. - // We could think of the teamFeatures flag to just disable the UI. The actual effect of DWD on credentials is disabled by toggling DWD off from UI - const dwd = - await DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey({ + // In case we need to disable the effects of DelegationCredential on credential we need to toggle DelegationCredential off from organization settings. + // We could think of the teamFeatures flag to just disable the UI. The actual effect of DelegationCredential on credentials is disabled by toggling DelegationCredential off from UI + const delegationCredential = + await DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey({ email: user.email, }); - if (!dwd || !dwd.enabled) { + if (!delegationCredential || !delegationCredential.enabled) { return []; } - const domainWideDelegationCredentials = [ - _buildDwdCalendarCredential({ dwd, user }), - _buildDwdConferencingCredential({ dwd, user }), + const delegationCredentials = [ + _buildDelegatedCalendarCredential({ delegationCredential, user }), + _buildDelegatedConferencingCredential({ delegationCredential, user }), ].filter((credential): credential is NonNullable => credential !== null); - log.debug("Returned", safeStringify({ domainWideDelegationCredentials })); - return domainWideDelegationCredentials; + log.debug("Returned", safeStringify({ delegationCredentials })); + return delegationCredentials; } -export async function getAllDwdCalendarCredentialsForUser({ user }: { user: { email: string; id: number } }) { - const dwdCredentials = await getAllDwdCredentialsForUser({ user }); - return dwdCredentials.filter((credential) => credential.type.endsWith("_calendar")); +export async function getAllDelegatedCalendarCredentialsForUser({ + user, +}: { + user: { email: string; id: number }; +}) { + const delegationCredentials = await getAllDelegationCredentialsForUser({ user }); + return delegationCredentials.filter((credential) => credential.type.endsWith("_calendar")); } -async function _getDwdCredentialsMapPerUser({ +async function _getDelegationCredentialsMapPerUser({ organizationId, users, }: { organizationId: number | null; users: User[]; }) { - const emptyMap = new Map>[]>(); + const emptyMap = new Map>[]>(); if (!organizationId) { return emptyMap; } const domain = users[0].email.split("@")[1]; log.debug("called with", safeStringify({ users })); - const dwd = - await DomainWideDelegationRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey( + const delegationCredential = + await DelegationCredentialRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey( { organizationId, domain, } ); - if (!dwd || !dwd.enabled) { + if (!delegationCredential || !delegationCredential.enabled) { return emptyMap; } const credentialsByUserId = new Map< number, - NonNullable>[] + NonNullable>[] >(); for (const user of users) { - const domainWideDelegationCredentials = [ - _buildDwdCalendarCredential({ dwd, user }), - _buildDwdConferencingCredential({ dwd, user }), + const delegationCredentials = [ + _buildDelegatedCalendarCredential({ delegationCredential, user }), + _buildDelegatedConferencingCredential({ delegationCredential, user }), ].filter((credential): credential is NonNullable => credential !== null); - log.debug("Returned for user", safeStringify({ user, domainWideDelegationCredentials })); - credentialsByUserId.set(user.id, domainWideDelegationCredentials); + log.debug("Returned for user", safeStringify({ user, delegationCredentials })); + credentialsByUserId.set(user.id, delegationCredentials); } return credentialsByUserId; } export async function checkIfSuccessfullyConfiguredInWorkspace({ - dwd, + delegationCredential, user, }: { - dwd: DomainWideDelegationWithSensitiveServiceAccountKey; + delegationCredential: DelegationCredentialWithSensitiveServiceAccountKey; user: User; }) { - if (!isValidWorkspaceSlug(dwd.workspacePlatform.slug)) { + if (!isValidWorkspaceSlug(delegationCredential.workspacePlatform.slug)) { log.warn( `Only ${WORKSPACE_PLATFORM_SLUGS.toString()} Platforms are supported here, skipping ${ - dwd.workspacePlatform.slug + delegationCredential.workspacePlatform.slug }` ); return false; } - const credential = _buildDwdCalendarCredentialWithServiceAccountKey({ - dwd, + const credential = _buildDelegatedCalendarCredentialWithServiceAccountKey({ + delegationCredential, user, }); @@ -241,31 +272,31 @@ export async function checkIfSuccessfullyConfiguredInWorkspace({ if (!calendar) { throw new Error("Google Calendar App not found"); } - return await calendar?.testDomainWideDelegationSetup?.(); + return await calendar?.testDelegationCredentialSetup?.(); } -export async function getAllDwdCredentialsForUserByAppType({ +export async function getAllDelegationCredentialsForUserByAppType({ user, appType, }: { user: User; appType: string; }) { - const dwdCredentials = await getAllDwdCredentialsForUser({ + const delegationCredentials = await getAllDelegationCredentialsForUser({ user, }); - return dwdCredentials.filter((credential) => credential.type === appType); + return delegationCredentials.filter((credential) => credential.type === appType); } -export async function getAllDwdCredentialsForUserByAppSlug({ +export async function getAllDelegationCredentialsForUserByAppSlug({ user, appSlug, }: { user: User; appSlug: string; }) { - const dwdCredentials = await getAllDwdCredentialsForUser({ user }); - return dwdCredentials.filter((credential) => credential.appId === appSlug); + const delegationCredentials = await getAllDelegationCredentialsForUser({ user }); + return delegationCredentials.filter((credential) => credential.appId === appSlug); } type Host = { @@ -274,19 +305,21 @@ type Host { - const nonDwdCredentials = existingCredentials.filter((cred) => !isDwdCredential({ credentialId: cred.id })); + const nonDelegationCredentials = existingCredentials.filter( + (cred) => !isDelegationCredential({ credentialId: cred.id }) + ); const allCredentials: CredentialForCalendarService[] = [ - ...dwdCredentials, - ...buildNonDwdCredentials(nonDwdCredentials), + ...delegationCredentials, + ...buildNonDelegationCredentials(nonDelegationCredentials), ]; const uniqueAllCredentials = allCredentials.reduce((acc, credential) => { @@ -295,10 +328,10 @@ export const buildAllCredentials = ({ acc.push(credential); return acc; } - const existingDwdCredential = acc.find( + const existingDelegationCredential = acc.find( (c) => c.delegatedToId === credential.delegatedToId && c.appId === credential.appId ); - if (!existingDwdCredential) { + if (!existingDelegationCredential) { acc.push(credential); } return acc; @@ -307,10 +340,10 @@ export const buildAllCredentials = ({ return uniqueAllCredentials; }; -export async function enrichUsersWithDwdCredentials< +export async function enrichUsersWithDelegationCredentials< TUser extends { id: number; email: string; credentials: CredentialPayload[] } >({ orgId, users }: { orgId: number | null; users: TUser[] }) { - const dwdCredentialsMap = await _getDwdCredentialsMapPerUser({ + const delegationCredentialsMap = await _getDelegationCredentialsMapPerUser({ organizationId: orgId, users, }); @@ -318,7 +351,7 @@ export async function enrichUsersWithDwdCredentials< const enrichedUsers = users.map((user) => { const { credentials, ...rest } = user; const enrichedCredentials = buildAllCredentials({ - dwdCredentials: dwdCredentialsMap.get(user.id) ?? [], + delegationCredentials: delegationCredentialsMap.get(user.id) ?? [], existingCredentials: credentials, }); return { @@ -326,11 +359,11 @@ export async function enrichUsersWithDwdCredentials< credentials: enrichedCredentials, }; }); - log.debug("enrichUsersWithDwdCredentials", safeStringify({ enrichedUsers, orgId })); + log.debug("enrichUsersWithDelegationCredentials", safeStringify({ enrichedUsers, orgId })); return enrichedUsers; } -export const enrichHostsWithDwdCredentials = async < +export const enrichHostsWithDelegationCredentials = async < THost extends Host, TUser extends { id: number; email: string; credentials: CredentialPayload[] } >({ @@ -340,7 +373,7 @@ export const enrichHostsWithDwdCredentials = async < orgId: number | null; hosts: THost[]; }) => { - const dwdCredentialsMap = await _getDwdCredentialsMapPerUser({ + const delegationCredentialsMap = await _getDelegationCredentialsMapPerUser({ organizationId: orgId, users: hosts.map((host) => host.user), }); @@ -352,38 +385,38 @@ export const enrichHostsWithDwdCredentials = async < user: { ...restUser, credentials: buildAllCredentials({ - dwdCredentials: dwdCredentialsMap.get(restUser.id) ?? [], + delegationCredentials: delegationCredentialsMap.get(restUser.id) ?? [], existingCredentials: credentials, }), }, }; }); - log.debug("enrichHostsWithDwdCredentials", safeStringify({ enrichedHosts, orgId })); + log.debug("enrichHostsWithDelegationCredentials", safeStringify({ enrichedHosts, orgId })); return enrichedHosts; }; -export const enrichUserWithDwdCredentialsWithoutOrgId = async < +export const enrichUserWithDelegationCredentialsWithoutOrgId = async < TUser extends { id: number; email: string; credentials: CredentialPayload[] } >({ user, }: { user: TUser; }) => { - const dwdCredentials = await getAllDwdCredentialsForUser({ user }); + const delegationCredentials = await getAllDelegationCredentialsForUser({ user }); const { credentials, ...restUser } = user; return { ...restUser, credentials: buildAllCredentials({ - dwdCredentials: dwdCredentials, + delegationCredentials: delegationCredentials, existingCredentials: credentials, }), }; }; -export async function enrichUserWithDwdConferencingCredentialsWithoutOrgId< +export async function enrichUserWithDelegationConferencingCredentialsWithoutOrgId< TUser extends { id: number; email: string; credentials: CredentialPayload[] } >({ user }: { user: TUser }) { - const { credentials, ...restUser } = await enrichUserWithDwdCredentialsWithoutOrgId({ user }); + const { credentials, ...restUser } = await enrichUserWithDelegationCredentialsWithoutOrgId({ user }); return { ...restUser, credentials: credentials.filter(_isConferencingCredential), @@ -391,20 +424,20 @@ export async function enrichUserWithDwdConferencingCredentialsWithoutOrgId< } /** - * Either get DWD credential from dwdCredentials or find regular credential from Credential table + * Either get Delegation credential from delegationCredentials or find regular credential from Credential table */ -export async function getDwdOrFindRegularCredential({ +export async function getDelegationCredentialOrFindRegularCredential({ id, - dwdCredentials, + delegationCredentials, }: { id: { credentialId: number | null | undefined; - domainWideDelegationCredentialId: string | null | undefined; + delegationCredentialId: string | null | undefined; }; - dwdCredentials: CredentialForCalendarService[]; + delegationCredentials: CredentialForCalendarService[]; }) { - return id.domainWideDelegationCredentialId - ? dwdCredentials.find((cred) => cred.delegatedToId === id.domainWideDelegationCredentialId) + return id.delegationCredentialId + ? delegationCredentials.find((cred) => cred.delegatedToId === id.delegationCredentialId) : id.credentialId ? await CredentialRepository.findCredentialForCalendarServiceById({ id: id.credentialId, @@ -413,20 +446,22 @@ export async function getDwdOrFindRegularCredential({ } /** - * Utility function to find a credential from a list of credentials, supporting both regular and DWD credentials + * Utility function to find a credential from a list of credentials, supporting both regular and DelegationCredential credentials */ -export function getDwdOrRegularCredential({ +export function getDelegationCredentialOrRegularCredential< + TCredential extends { delegatedToId?: string | null; id: number } +>({ credentials, id, }: { credentials: TCredential[]; - id: { credentialId: number | null | undefined; dwdId: string | null | undefined }; + id: { credentialId: number | null | undefined; delegationCredentialId: string | null | undefined }; }) { return ( credentials.find((cred) => { // Ensure that we don't match null to null if (cred.delegatedToId) { - return cred.delegatedToId === id.dwdId; + return cred.delegatedToId === id.delegationCredentialId; } else if (id.credentialId) { return cred.id === id.credentialId; } @@ -435,7 +470,7 @@ export function getDwdOrRegularCredential _isConferencingCredential(credential)); } -export function getFirstDwdConferencingCredentialAppLocation({ +export function getFirstDelegationConferencingCredentialAppLocation({ credentials, }: { credentials: CredentialForCalendarService[]; }) { - const dwdConferencingCredential = getFirstDwdConferencingCredential({ credentials }); - if (dwdConferencingCredential?.appId === googleMeetMetadata.slug) { + const delegatedConferencingCredential = getFirstDelegationConferencingCredential({ credentials }); + if (delegatedConferencingCredential?.appId === googleMeetMetadata.slug) { return googleMeetMetadata.appData?.location?.type ?? null; } - if (dwdConferencingCredential?.appId === office365VideoMetaData.slug) { + if (delegatedConferencingCredential?.appId === office365VideoMetaData.slug) { return office365VideoMetaData.appData?.location?.type ?? null; } return null; diff --git a/packages/lib/domainWideDelegation/clientAndServer.ts b/packages/lib/domainWideDelegation/clientAndServer.ts deleted file mode 100644 index 5829191fbe..0000000000 --- a/packages/lib/domainWideDelegation/clientAndServer.ts +++ /dev/null @@ -1,27 +0,0 @@ -export function isDwdCredential({ credentialId }: { credentialId: number | null | undefined }) { - // Though it is set as -1 right now, but we might want to set it to some other negative value. - return typeof credentialId === "number" && credentialId < 0; -} - -export const buildNonDwdCredential = | null>(credential: T) => { - type WithDelegatedCredential = T extends null - ? null - : T & { - delegatedTo: null; - delegatedToId: null; - }; - - if (!credential) return null as WithDelegatedCredential; - return { - ...credential, - delegatedTo: null, - delegatedToId: null, - } as WithDelegatedCredential; -}; - -export const buildNonDwdCredentials = >(credentials: T[]) => { - return credentials.map(buildNonDwdCredential).filter((credential) => !!credential) as (T & { - delegatedTo: null; - delegatedToId: null; - })[]; -}; diff --git a/packages/lib/getConnectedApps.ts b/packages/lib/getConnectedApps.ts index 20955b69a4..8891e0b7e0 100644 --- a/packages/lib/getConnectedApps.ts +++ b/packages/lib/getConnectedApps.ts @@ -13,7 +13,7 @@ import type { TeamQuery } from "@calcom/trpc/server/routers/loggedInViewer/integ import type { TIntegrationsInputSchema } from "@calcom/trpc/server/routers/loggedInViewer/integrations.schema"; import type { PaymentApp } from "@calcom/types/PaymentService"; -import { buildNonDwdCredentials } from "./domainWideDelegation/clientAndServer"; +import { buildNonDelegationCredentials } from "./delegationCredential/clientAndServer"; export type ConnectedApps = Awaited>; @@ -106,7 +106,7 @@ export async function getConnectedApps({ userTeams = [...teamsQuery, ...parentTeams]; const teamAppCredentials = userTeams.flatMap((teamApp) => { - return teamApp.credentials ? buildNonDwdCredentials(teamApp.credentials.flat()) : []; + return teamApp.credentials ? buildNonDelegationCredentials(teamApp.credentials.flat()) : []; }); if (!includeTeamInstalledApps || teamId) { credentials = teamAppCredentials; diff --git a/packages/lib/getConnectedDestinationCalendars.ts b/packages/lib/getConnectedDestinationCalendars.ts index b3844c32ca..c3c1fb721e 100644 --- a/packages/lib/getConnectedDestinationCalendars.ts +++ b/packages/lib/getConnectedDestinationCalendars.ts @@ -1,6 +1,6 @@ import { getCalendarCredentials, getConnectedCalendars } from "@calcom/lib/CalendarManager"; -import { isDwdCredential } from "@calcom/lib/domainWideDelegation/clientAndServer"; -import { enrichUserWithDwdCredentialsWithoutOrgId } from "@calcom/lib/domainWideDelegation/server"; +import { isDelegationCredential } from "@calcom/lib/delegationCredential/clientAndServer"; +import { enrichUserWithDelegationCredentialsWithoutOrgId } from "@calcom/lib/delegationCredential/server"; import logger from "@calcom/lib/logger"; import type { PrismaClient } from "@calcom/prisma"; import prisma from "@calcom/prisma"; @@ -27,14 +27,14 @@ export type ConnectedDestinationCalendars = Awaited< >; /** - * Ensures that when DWD is enabled and there is already a calendar connected for the corresponding domain, we only allow the DWD calendar to be returned - * This is to ensure that duplicate calendar connections aren't shown in UI(apps/installed/calendars). We choose DWD connection to be shown because we don't want users to be able to work with individual calendars + * Ensures that when DelegationCredential is enabled and there is already a calendar connected for the corresponding domain, we only allow the DelegationCredential calendar to be returned + * This is to ensure that duplicate calendar connections aren't shown in UI(apps/installed/calendars). We choose DelegationCredential connection to be shown because we don't want users to be able to work with individual calendars */ -const _ensureNoConflictingNonDwdConnectedCalendar = < +const _ensureNoConflictingNonDelegatedConnectedCalendar = < T extends { integration: { slug: string }; primary?: { email?: string | null | undefined } | undefined; - domainWideDelegationCredentialId?: string | null | undefined; + delegationCredentialId?: string | null | undefined; } >({ connectedCalendars, @@ -51,18 +51,18 @@ const _ensureNoConflictingNonDwdConnectedCalendar = < // If no other calendar with this slug, keep it if (allCalendarsWithSameAppSlug.length === 1) return true; - const dwdCalendarsWithSameAppSlug = allCalendarsWithSameAppSlug.filter( - (cal) => cal.domainWideDelegationCredentialId + const delegatedCalendarsWithSameAppSlug = allCalendarsWithSameAppSlug.filter( + (cal) => cal.delegationCredentialId ); - if (!dwdCalendarsWithSameAppSlug.length) { + if (!delegatedCalendarsWithSameAppSlug.length) { return true; } - if (connectedCalendar.domainWideDelegationCredentialId) { + if (connectedCalendar.delegationCredentialId) { return true; } - // DWD Credential is always of the loggedInUser + // DelegationCredential Credential is always of the loggedInUser if (!connectedCalendar.primary?.email || connectedCalendar.primary.email !== loggedInUser.email) { return true; } @@ -116,7 +116,7 @@ async function handleNoDestinationCalendar({ integration = "", externalId = "", credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, email: primaryEmail, } = connectedCalendars[0].primary ?? {}; @@ -140,12 +140,12 @@ async function handleNoDestinationCalendar({ integration, externalId, primaryEmail, - ...(!isDwdCredential({ credentialId }) + ...(!isDelegationCredential({ credentialId }) ? { credentialId, } : { - domainWideDelegationCredentialId, + delegationCredentialId, }), }, }); @@ -285,7 +285,7 @@ export async function getConnectedDestinationCalendarsAndEnsureDefaultsInDb({ select: credentialForCalendarServiceSelect, }); - const { credentials: allCredentials } = await enrichUserWithDwdCredentialsWithoutOrgId({ + const { credentials: allCredentials } = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: { id: user.id, email: user.email, credentials: userCredentials }, }); @@ -363,12 +363,12 @@ export async function getConnectedDestinationCalendarsAndEnsureDefaultsInDb({ }); } - const noConflictingNonDwdConnectedCalendars = _ensureNoConflictingNonDwdConnectedCalendar({ + const noConflictingNonDelegatedConnectedCalendars = _ensureNoConflictingNonDelegatedConnectedCalendar({ connectedCalendars, loggedInUser: { email: user.email }, }); return { - connectedCalendars: noConflictingNonDwdConnectedCalendars, + connectedCalendars: noConflictingNonDelegatedConnectedCalendars, destinationCalendar: { ...(user.destinationCalendar as DestinationCalendar), ...destinationCalendar, diff --git a/packages/lib/payment/getBooking.ts b/packages/lib/payment/getBooking.ts index 583746a0d2..267bbd7c6e 100644 --- a/packages/lib/payment/getBooking.ts +++ b/packages/lib/payment/getBooking.ts @@ -9,7 +9,7 @@ import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/crede import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { CalendarEvent } from "@calcom/types/Calendar"; -import { enrichUserWithDwdCredentialsWithoutOrgId } from "../domainWideDelegation/server"; +import { enrichUserWithDelegationCredentialsWithoutOrgId } from "../delegationCredential/server"; import { getBookerBaseUrl } from "../getBookerUrl/server"; async function getEventType(id: number) { @@ -103,11 +103,11 @@ export async function getBooking(bookingId: number) { const eventType = { ...eventTypeRaw, metadata: EventTypeMetaDataSchema.parse(eventTypeRaw?.metadata) }; - const { user: userWithoutDwdCredentials } = booking; + const { user: userWithoutDelegationCredentials } = booking; - if (!userWithoutDwdCredentials) throw new HttpCode({ statusCode: 204, message: "No user found" }); - const user = await enrichUserWithDwdCredentialsWithoutOrgId({ - user: userWithoutDwdCredentials, + if (!userWithoutDelegationCredentials) throw new HttpCode({ statusCode: 204, message: "No user found" }); + const user = await enrichUserWithDelegationCredentialsWithoutOrgId({ + user: userWithoutDelegationCredentials, }); const t = await getTranslation(user.locale ?? "en", "common"); diff --git a/packages/lib/server/buildCredentialPayloadForCalendar.ts b/packages/lib/server/buildCredentialPayloadForCalendar.ts index d23b4f07bf..8221b778ac 100644 --- a/packages/lib/server/buildCredentialPayloadForCalendar.ts +++ b/packages/lib/server/buildCredentialPayloadForCalendar.ts @@ -1,28 +1,28 @@ -import { isDwdCredential } from "@calcom/lib/domainWideDelegation/clientAndServer"; +import { isDelegationCredential } from "@calcom/lib/delegationCredential/clientAndServer"; export function buildCredentialPayloadForPrisma({ credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, }: { credentialId: number | null | undefined; - domainWideDelegationCredentialId: string | null | undefined; + delegationCredentialId: string | null | undefined; }) { if (credentialId === undefined) { return { credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, }; } - if (isDwdCredential({ credentialId })) { + if (isDelegationCredential({ credentialId })) { return { credentialId: null, - domainWideDelegationCredentialId, + delegationCredentialId, }; } else { return { credentialId, - domainWideDelegationCredentialId: null, + delegationCredentialId: null, }; } } diff --git a/packages/lib/server/findUsersForAvailabilityCheck.ts b/packages/lib/server/findUsersForAvailabilityCheck.ts index b6710c6c0c..ae6f3a7a59 100644 --- a/packages/lib/server/findUsersForAvailabilityCheck.ts +++ b/packages/lib/server/findUsersForAvailabilityCheck.ts @@ -1,6 +1,6 @@ import type { Prisma } from "@prisma/client"; -import { enrichUserWithDwdCredentialsWithoutOrgId } from "@calcom/lib/domainWideDelegation/server"; +import { enrichUserWithDelegationCredentialsWithoutOrgId } from "@calcom/lib/delegationCredential/server"; import { availabilityUserSelect } from "@calcom/prisma"; import { prisma } from "@calcom/prisma"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; @@ -23,7 +23,7 @@ export async function findUsersForAvailabilityCheck({ where }: { where: Prisma.U return null; } - return await enrichUserWithDwdCredentialsWithoutOrgId({ + return await enrichUserWithDelegationCredentialsWithoutOrgId({ user: withSelectedCalendars(user), }); } diff --git a/packages/lib/server/getUsersCredentials.ts b/packages/lib/server/getUsersCredentials.ts index f3ff81ba13..1db7315d43 100644 --- a/packages/lib/server/getUsersCredentials.ts +++ b/packages/lib/server/getUsersCredentials.ts @@ -2,13 +2,13 @@ import { prisma } from "@calcom/prisma"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; -import { enrichUserWithDwdCredentialsWithoutOrgId } from "../domainWideDelegation/server"; +import { enrichUserWithDelegationCredentialsWithoutOrgId } from "../delegationCredential/server"; type SessionUser = NonNullable; type User = { id: SessionUser["id"]; email: SessionUser["email"] }; /** - * It includes in-memory DWD credentials as well. + * It includes in-memory DelegationCredential credentials as well. */ export async function getUsersCredentials(user: User) { const credentials = await prisma.credential.findMany({ @@ -21,7 +21,7 @@ export async function getUsersCredentials(user: User) { }, }); - const { credentials: allCredentials } = await enrichUserWithDwdCredentialsWithoutOrgId({ + const { credentials: allCredentials } = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: { email: user.email, id: user.id, diff --git a/packages/lib/server/repository/__tests__/domainWideDelegation.test.ts b/packages/lib/server/repository/__tests__/delegationCredential.test.ts similarity index 83% rename from packages/lib/server/repository/__tests__/domainWideDelegation.test.ts rename to packages/lib/server/repository/__tests__/delegationCredential.test.ts index 65b9aa535c..738bc402dc 100644 --- a/packages/lib/server/repository/__tests__/domainWideDelegation.test.ts +++ b/packages/lib/server/repository/__tests__/delegationCredential.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it, beforeEach, vi } from "vitest"; import { encryptServiceAccountKey } from "@calcom/lib/server/serviceAccountKey"; -import { DomainWideDelegationRepository } from "../domainWideDelegation"; +import { DelegationCredentialRepository } from "../delegationCredential"; import { OrganizationRepository } from "../organization"; vi.mock("../organization", () => ({ @@ -56,7 +56,7 @@ const buildMockOrganization = () => ({ isOrganization: true, }); -const buildMockDomainWideDelegation = (overrides = {}) => ({ +const buildMockDelegationCredential = (overrides = {}) => ({ id: "test-id", enabled: true, domain: "example.com", @@ -68,7 +68,7 @@ const buildMockDomainWideDelegation = (overrides = {}) => ({ }); // Test Utilities -const createTestDomainWideDelegation = async (overrides = {}) => { +const createTestDelegationCredential = async (overrides = {}) => { // Create organization first (as a team with isOrganization true) await prismock.team.create({ data: buildMockOrganization(), @@ -79,9 +79,9 @@ const createTestDomainWideDelegation = async (overrides = {}) => { data: buildMockWorkspacePlatform(), }); - const data = buildMockDomainWideDelegation(overrides); + const data = buildMockDelegationCredential(overrides); - return await prismock.domainWideDelegation.create({ + return await prismock.delegationCredential.create({ data: { ...data, serviceAccountKey: encryptServiceAccountKey(data.serviceAccountKey), @@ -96,9 +96,9 @@ const setupOrganizationMock = (returnValue: any) => { vi.mocked(OrganizationRepository.findByMemberEmail).mockResolvedValue(returnValue); }; -describe("DomainWideDelegationRepository", () => { +describe("DelegationCredentialRepository", () => { beforeEach(() => { - prismock.domainWideDelegation.deleteMany(); + prismock.delegationCredential.deleteMany(); prismock.workspacePlatform.deleteMany(); prismock.team.deleteMany(); vi.clearAllMocks(); @@ -106,13 +106,13 @@ describe("DomainWideDelegationRepository", () => { describe("Service Account Key Access Control", () => { beforeEach(async () => { - await createTestDomainWideDelegation(); + await createTestDelegationCredential(); }); describe("Regular Find Methods (No Service Account Key)", () => { describe("findById", () => { it("should not expose service account key in response", async () => { - const result = await DomainWideDelegationRepository.findById({ id: "test-id" }); + const result = await DelegationCredentialRepository.findById({ id: "test-id" }); expect(result).not.toHaveProperty("serviceAccountKey"); expect(result).toEqual( @@ -127,7 +127,7 @@ describe("DomainWideDelegationRepository", () => { describe("findAllByDomain", () => { it("should not expose service account key in any of the results", async () => { - const results = await DomainWideDelegationRepository.findAllByDomain({ domain: "example.com" }); + const results = await DelegationCredentialRepository.findAllByDomain({ domain: "example.com" }); expect(results.length).toBeGreaterThan(0); results.forEach((result) => { @@ -145,7 +145,7 @@ describe("DomainWideDelegationRepository", () => { describe("Sensitive Methods (With Service Account Key)", () => { describe("findByIdIncludeSensitiveServiceAccountKey", () => { it("should expose valid service account key", async () => { - const result = await DomainWideDelegationRepository.findByIdIncludeSensitiveServiceAccountKey({ + const result = await DelegationCredentialRepository.findByIdIncludeSensitiveServiceAccountKey({ id: "test-id", }); @@ -156,7 +156,7 @@ describe("DomainWideDelegationRepository", () => { describe("findByOrgIdIncludeSensitiveServiceAccountKey", () => { it("should expose validated service account keys", async () => { - const results = await DomainWideDelegationRepository.findByOrgIdIncludeSensitiveServiceAccountKey({ + const results = await DelegationCredentialRepository.findByOrgIdIncludeSensitiveServiceAccountKey({ organizationId: 1, }); @@ -170,7 +170,7 @@ describe("DomainWideDelegationRepository", () => { describe("findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey", () => { it("should expose validated service account key", async () => { const result = - await DomainWideDelegationRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey( + await DelegationCredentialRepository.findUniqueByOrganizationIdAndDomainIncludeSensitiveServiceAccountKey( { organizationId: 1, domain: "example.com", @@ -187,7 +187,7 @@ describe("DomainWideDelegationRepository", () => { setupOrganizationMock({ id: 1 }); const result = - await DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey({ + await DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey({ email: "user@example.com", }); @@ -200,7 +200,7 @@ describe("DomainWideDelegationRepository", () => { setupOrganizationMock(null); const result = - await DomainWideDelegationRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey({ + await DelegationCredentialRepository.findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey({ email: "nonexistent@example.com", }); @@ -212,7 +212,7 @@ describe("DomainWideDelegationRepository", () => { describe("CRUD Operations", () => { describe("create", () => { - it("should store domain wide delegation with all required fields and establish connections", async () => { + it("should store delegation credential with all required fields and establish connections", async () => { // Create organization first (as a team with isOrganization true) await prismock.team.create({ data: buildMockOrganization(), @@ -223,11 +223,11 @@ describe("DomainWideDelegationRepository", () => { data: buildMockWorkspacePlatform(), }); - const data = buildMockDomainWideDelegation(); + const data = buildMockDelegationCredential(); delete data.id; // ID is auto-generated delete data.workspacePlatform; // Don't include the relation in create - const result = await DomainWideDelegationRepository.create(data); + const result = await DelegationCredentialRepository.create(data); expect(result).toEqual({ id: expect.any(String), @@ -246,7 +246,7 @@ describe("DomainWideDelegationRepository", () => { describe("update", () => { it("should modify workspace platform connection when workspacePlatformId is provided", async () => { - const created = await createTestDomainWideDelegation(); + const created = await createTestDelegationCredential(); // Create new workspace platform const newWorkspacePlatform = await prismock.workspacePlatform.create({ @@ -257,7 +257,7 @@ describe("DomainWideDelegationRepository", () => { }, }); - const result = await DomainWideDelegationRepository.updateById({ + const result = await DelegationCredentialRepository.updateById({ id: created.id, data: { workspacePlatformId: newWorkspacePlatform.id }, }); @@ -277,7 +277,7 @@ describe("DomainWideDelegationRepository", () => { }); it("should modify organization connection when organizationId is provided", async () => { - const created = await createTestDomainWideDelegation(); + const created = await createTestDelegationCredential(); // Create new organization (as a team with isOrganization true) const newOrganization = await prismock.team.create({ @@ -288,7 +288,7 @@ describe("DomainWideDelegationRepository", () => { }, }); - const result = await DomainWideDelegationRepository.updateById({ + const result = await DelegationCredentialRepository.updateById({ id: created.id, data: { organizationId: newOrganization.id }, }); @@ -308,9 +308,9 @@ describe("DomainWideDelegationRepository", () => { }); it("should modify specified fields while preserving others", async () => { - const created = await createTestDomainWideDelegation(); + const created = await createTestDelegationCredential(); - const result = await DomainWideDelegationRepository.updateById({ + const result = await DelegationCredentialRepository.updateById({ id: created.id, data: { enabled: false }, }); @@ -326,12 +326,12 @@ describe("DomainWideDelegationRepository", () => { }); describe("delete", () => { - it("should remove the domain wide delegation completely", async () => { - const created = await createTestDomainWideDelegation(); + it("should remove the delegation credential completely", async () => { + const created = await createTestDelegationCredential(); - await DomainWideDelegationRepository.deleteById({ id: created.id }); + await DelegationCredentialRepository.deleteById({ id: created.id }); - const result = await prismock.domainWideDelegation.findUnique({ + const result = await prismock.delegationCredential.findUnique({ where: { id: created.id }, }); expect(result).toBeNull(); diff --git a/packages/lib/server/repository/credential.ts b/packages/lib/server/repository/credential.ts index 01531d8c57..ed865f91f8 100644 --- a/packages/lib/server/repository/credential.ts +++ b/packages/lib/server/repository/credential.ts @@ -2,7 +2,7 @@ import { prisma } from "@calcom/prisma"; import { safeCredentialSelect } from "@calcom/prisma/selects/credential"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; -import { buildNonDwdCredential } from "../../domainWideDelegation/server"; +import { buildNonDelegationCredential } from "../../delegationCredential/server"; type CredentialCreateInput = { type: string; @@ -14,7 +14,7 @@ type CredentialCreateInput = { export class CredentialRepository { static async create(data: CredentialCreateInput) { const credential = await prisma.credential.create({ data: { ...data } }); - return buildNonDwdCredential(credential); + return buildNonDelegationCredential(credential); } static async findByAppIdAndUserId({ appId, userId }: { appId: string; userId: number }) { const credential = await prisma.credential.findFirst({ @@ -23,7 +23,7 @@ export class CredentialRepository { userId, }, }); - return buildNonDwdCredential(credential); + return buildNonDelegationCredential(credential); } /** @@ -31,7 +31,7 @@ export class CredentialRepository { */ static async findFirstByIdWithUser({ id }: { id: number }) { const credential = await prisma.credential.findFirst({ where: { id }, select: safeCredentialSelect }); - return buildNonDwdCredential(credential); + return buildNonDelegationCredential(credential); } /** @@ -42,7 +42,7 @@ export class CredentialRepository { where: { id }, select: { ...safeCredentialSelect, key: true }, }); - return buildNonDwdCredential(credential); + return buildNonDelegationCredential(credential); } static async findFirstByAppIdAndUserId({ appId, userId }: { appId: string; userId: number }) { @@ -56,7 +56,7 @@ export class CredentialRepository { static async findFirstByUserIdAndType({ userId, type }: { userId: number; type: string }) { const credential = await prisma.credential.findFirst({ where: { userId, type } }); - return buildNonDwdCredential(credential); + return buildNonDelegationCredential(credential); } static async deleteById({ id }: { id: number }) { @@ -73,6 +73,6 @@ export class CredentialRepository { return dbCredential; } - return buildNonDwdCredential(dbCredential); + return buildNonDelegationCredential(dbCredential); } } diff --git a/packages/lib/server/repository/domainWideDelegation.ts b/packages/lib/server/repository/delegationCredential.ts similarity index 71% rename from packages/lib/server/repository/domainWideDelegation.ts rename to packages/lib/server/repository/delegationCredential.ts index 6e721bd66a..dd8ad6fb13 100644 --- a/packages/lib/server/repository/domainWideDelegation.ts +++ b/packages/lib/server/repository/delegationCredential.ts @@ -13,8 +13,8 @@ import { prisma } from "@calcom/prisma"; import { OrganizationRepository } from "./organization"; export type { ServiceAccountKey }; -const repositoryLogger = logger.getSubLogger({ prefix: ["DomainWideDelegationRepository"] }); -const domainWideDelegationSafeSelect = { +const repositoryLogger = logger.getSubLogger({ prefix: ["DelegationCredentialRepository"] }); +const delegationCredentialSafeSelect = { id: true, enabled: true, domain: true, @@ -29,12 +29,12 @@ const domainWideDelegationSafeSelect = { }, }; -const domainWideDelegationSelectIncludesServiceAccountKey = { - ...domainWideDelegationSafeSelect, +const delegationCredentialSelectIncludesServiceAccountKey = { + ...delegationCredentialSafeSelect, serviceAccountKey: true, }; -export class DomainWideDelegationRepository { +export class DelegationCredentialRepository { private static encryptServiceAccountKey(serviceAccountKey: ServiceAccountKey): EncryptedServiceAccountKey { return encryptServiceAccountKey(serviceAccountKey); } @@ -44,12 +44,12 @@ export class DomainWideDelegationRepository { } private static withParsedServiceAccountKey( - domainWideDelegation: T + delegationCredential: T ) { - if (!domainWideDelegation) { + if (!delegationCredential) { return null; } - const { serviceAccountKey, ...rest } = domainWideDelegation; + const { serviceAccountKey, ...rest } = delegationCredential; // Decrypt the service account key if it exists const decryptedKey = this.decryptServiceAccountKey(serviceAccountKey); @@ -69,7 +69,7 @@ export class DomainWideDelegationRepository { serviceAccountKey: ServiceAccountKey; }) { const encryptedKey = this.encryptServiceAccountKey(data.serviceAccountKey); - return await prisma.domainWideDelegation.create({ + return await prisma.delegationCredential.create({ data: { workspacePlatform: { connect: { @@ -85,14 +85,14 @@ export class DomainWideDelegationRepository { }, serviceAccountKey: encryptedKey, }, - select: domainWideDelegationSafeSelect, + select: delegationCredentialSafeSelect, }); } static async findById({ id }: { id: string }) { - return await prisma.domainWideDelegation.findUnique({ + return await prisma.delegationCredential.findUnique({ where: { id }, - select: domainWideDelegationSafeSelect, + select: delegationCredentialSafeSelect, }); } @@ -103,20 +103,20 @@ export class DomainWideDelegationRepository { organizationId: number; domain: string; }) { - const dwd = await prisma.domainWideDelegation.findUnique({ + const delegationCredential = await prisma.delegationCredential.findUnique({ where: { organizationId_domain: { organizationId, domain } }, - select: domainWideDelegationSelectIncludesServiceAccountKey, + select: delegationCredentialSelectIncludesServiceAccountKey, }); - return DomainWideDelegationRepository.withParsedServiceAccountKey(dwd); + return DelegationCredentialRepository.withParsedServiceAccountKey(delegationCredential); } static async findByIdIncludeSensitiveServiceAccountKey({ id }: { id: string }) { - const domainWideDelegation = await prisma.domainWideDelegation.findUnique({ + const delegationCredential = await prisma.delegationCredential.findUnique({ where: { id }, - select: domainWideDelegationSelectIncludesServiceAccountKey, + select: delegationCredentialSelectIncludesServiceAccountKey, }); - if (!domainWideDelegation) return null; - return DomainWideDelegationRepository.withParsedServiceAccountKey(domainWideDelegation); + if (!delegationCredential) return null; + return DelegationCredentialRepository.withParsedServiceAccountKey(delegationCredential); } static async findUniqueByOrgMemberEmailIncludeSensitiveServiceAccountKey({ email }: { email: string }) { @@ -131,23 +131,23 @@ export class DomainWideDelegationRepository { } const emailDomain = email.split("@")[1]; - const domainWideDelegation = await prisma.domainWideDelegation.findUnique({ + const delegationCredential = await prisma.delegationCredential.findUnique({ where: { organizationId_domain: { organizationId: organization.id, domain: emailDomain, }, }, - select: domainWideDelegationSelectIncludesServiceAccountKey, + select: delegationCredentialSelectIncludesServiceAccountKey, }); - return DomainWideDelegationRepository.withParsedServiceAccountKey(domainWideDelegation); + return DelegationCredentialRepository.withParsedServiceAccountKey(delegationCredential); } static async findAllByDomain({ domain }: { domain: string }) { - return await prisma.domainWideDelegation.findMany({ + return await prisma.delegationCredential.findMany({ where: { domain }, - select: domainWideDelegationSafeSelect, + select: delegationCredentialSafeSelect, }); } @@ -164,7 +164,7 @@ export class DomainWideDelegationRepository { }>; }) { const { workspacePlatformId, organizationId, ...rest } = data; - return await prisma.domainWideDelegation.update({ + return await prisma.delegationCredential.update({ where: { id }, data: { ...(workspacePlatformId && { @@ -183,21 +183,21 @@ export class DomainWideDelegationRepository { }), ...rest, }, - select: domainWideDelegationSafeSelect, + select: delegationCredentialSafeSelect, }); } static async deleteById({ id }: { id: string }) { - return await prisma.domainWideDelegation.delete({ + return await prisma.delegationCredential.delete({ where: { id }, }); } static async findByOrgIdIncludeSensitiveServiceAccountKey({ organizationId }: { organizationId: number }) { - return await prisma.domainWideDelegation.findMany({ + return await prisma.delegationCredential.findMany({ where: { organizationId }, select: { - ...domainWideDelegationSelectIncludesServiceAccountKey, + ...delegationCredentialSelectIncludesServiceAccountKey, workspacePlatform: { select: { id: true, diff --git a/packages/lib/server/repository/destinationCalendar.ts b/packages/lib/server/repository/destinationCalendar.ts index 9848e789d4..37de4b0a57 100644 --- a/packages/lib/server/repository/destinationCalendar.ts +++ b/packages/lib/server/repository/destinationCalendar.ts @@ -44,24 +44,24 @@ export class DestinationCalendarRepository { externalId?: string; credentialId?: number | null; primaryEmail?: string | null; - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; }; create: { integration: string; externalId: string; credentialId: number | null; primaryEmail?: string | null; - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; }; }) { const credentialPayloadForUpdate = buildCredentialPayloadForPrisma({ credentialId: update.credentialId, - domainWideDelegationCredentialId: update.domainWideDelegationCredentialId, + delegationCredentialId: update.delegationCredentialId, }); const credentialPayloadForCreate = buildCredentialPayloadForPrisma({ credentialId: create.credentialId, - domainWideDelegationCredentialId: create.domainWideDelegationCredentialId, + delegationCredentialId: create.delegationCredentialId, }); return await prisma.destinationCalendar.upsert({ diff --git a/packages/lib/server/repository/selectedCalendar.test.ts b/packages/lib/server/repository/selectedCalendar.test.ts index 58468d2d94..025863259a 100644 --- a/packages/lib/server/repository/selectedCalendar.test.ts +++ b/packages/lib/server/repository/selectedCalendar.test.ts @@ -290,13 +290,13 @@ describe("SelectedCalendarRepository", () => { }); }); - describe("Domain Wide Delegation", () => { - it("should create a selected calendar with domainWideDelegationCredentialId", async () => { + describe("Delegation Credential", () => { + it("should create a selected calendar with delegationCredentialId", async () => { const data = { userId: 1, integration: "google_calendar", externalId: "test@gmail.com", - domainWideDelegationCredentialId: "dwd-123", + delegationCredentialId: "delegationCredential-123", }; const result = await SelectedCalendarRepository.create(data); @@ -306,7 +306,7 @@ describe("SelectedCalendarRepository", () => { describe("upsert", () => { describe("updation", () => { - it("should update existing record with domainWideDelegationCredentialId if credentialId is -1", async () => { + it("should update existing record with delegationCredentialId if credentialId is -1", async () => { const initialData = { userId: 1, integration: "google_calendar", @@ -322,17 +322,17 @@ describe("SelectedCalendarRepository", () => { integration: "google_calendar", externalId: "test@gmail.com", credentialId: -1, - domainWideDelegationCredentialId: "dwd-123", + delegationCredentialId: "delegationCredential-123", }; const result = await SelectedCalendarRepository.upsert(data); expect(result.id).not.toBe(null); expect(result.id).toBe(existingCalendar.id); expect(result.credentialId).toBe(null); - expect(result.domainWideDelegationCredentialId).toBe(data.domainWideDelegationCredentialId); + expect(result.delegationCredentialId).toBe(data.delegationCredentialId); }); - it("should update existing record with credentialId if credentialId is valid(>0) even if domainWideDelegationCredentialId is set", async () => { + it("should update existing record with credentialId if credentialId is valid(>0) even if delegationCredentialId is set", async () => { const initialData = { userId: 1, integration: "google_calendar", @@ -348,19 +348,19 @@ describe("SelectedCalendarRepository", () => { integration: "google_calendar", externalId: "test@gmail.com", credentialId: 2, - domainWideDelegationCredentialId: "dwd-123", + delegationCredentialId: "delegationCredential-123", }; const result = await SelectedCalendarRepository.upsert(data); expect(result.id).not.toBe(null); expect(result.id).toBe(existingCalendar.id); expect(result.credentialId).toBe(data.credentialId); - expect(result.domainWideDelegationCredentialId).toBeNull(); + expect(result.delegationCredentialId).toBeNull(); }); }); describe("creation", () => { - it("should create a new record with domainWideDelegationCredentialId if credentialId is -1", async () => { + it("should create a new record with delegationCredentialId if credentialId is -1", async () => { const initialData = { userId: 1, integration: "google_calendar", @@ -376,7 +376,7 @@ describe("SelectedCalendarRepository", () => { integration: "google_calendar", externalId: "anotheremail@gmail.com", credentialId: -1, - domainWideDelegationCredentialId: "dwd-123", + delegationCredentialId: "delegationCredential-123", }; // It will create a new record because of unique constraint violation @@ -384,17 +384,17 @@ describe("SelectedCalendarRepository", () => { expect(result.id).not.toBe(null); expect(result.id).not.toBe(existingCalendar.id); expect(result.credentialId).toBe(null); - expect(result.domainWideDelegationCredentialId).toBe(data.domainWideDelegationCredentialId); + expect(result.delegationCredentialId).toBe(data.delegationCredentialId); }); }); - it("shouldnt update existing domainWideDelegationCredentialId if upsert data doesn't have it", async () => { + it("shouldnt update existing delegationCredentialId if upsert data doesn't have it", async () => { const initialData = { userId: 1, integration: "google_calendar", externalId: "test@gmail.com", eventTypeId: null, - domainWideDelegationCredentialId: "dwd-123", + delegationCredentialId: "delegationCredential-123", credentialId: 1, }; @@ -410,16 +410,16 @@ describe("SelectedCalendarRepository", () => { const result = await SelectedCalendarRepository.upsert(data); expect(result.id).toBe(existingCalendar.id); expect(result.credentialId).toBe(existingCalendar.credentialId); - expect(result.domainWideDelegationCredentialId).toBe(null); + expect(result.delegationCredentialId).toBe(null); }); - it("shouldnt update domainWideDelegationCredentialId if it is undefined", async () => { + it("shouldnt update delegationCredentialId if it is undefined", async () => { const initialData = { userId: 1, integration: "google_calendar", externalId: "test@gmail.com", eventTypeId: null, - domainWideDelegationCredentialId: "dwd-123", + delegationCredentialId: "delegationCredential-123", }; const existingCalendar = await SelectedCalendarRepository.create(initialData); @@ -433,9 +433,7 @@ describe("SelectedCalendarRepository", () => { const result = await SelectedCalendarRepository.upsert(data); expect(result.id).toBe(existingCalendar.id); expect(result.credentialId).toBe(existingCalendar.credentialId); - expect(result.domainWideDelegationCredentialId).toBe( - existingCalendar.domainWideDelegationCredentialId - ); + expect(result.delegationCredentialId).toBe(existingCalendar.delegationCredentialId); }); }); }); diff --git a/packages/lib/server/repository/selectedCalendar.ts b/packages/lib/server/repository/selectedCalendar.ts index 8643023a2b..39502062ab 100644 --- a/packages/lib/server/repository/selectedCalendar.ts +++ b/packages/lib/server/repository/selectedCalendar.ts @@ -82,7 +82,7 @@ export class SelectedCalendarRepository { // So, this unique constraint can't be used in upsert. Prisma doesn't allow that, So, we do create and update separately const credentialPayload = buildCredentialPayloadForPrisma({ credentialId: data.credentialId, - domainWideDelegationCredentialId: data.domainWideDelegationCredentialId, + delegationCredentialId: data.delegationCredentialId, }); const newData = { diff --git a/packages/lib/server/repository/user.ts b/packages/lib/server/repository/user.ts index 4ac1f8ac36..a162bfb682 100644 --- a/packages/lib/server/repository/user.ts +++ b/packages/lib/server/repository/user.ts @@ -12,7 +12,7 @@ import { userMetadata } from "@calcom/prisma/zod-utils"; import type { UpId, UserProfile } from "@calcom/types/UserProfile"; import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "../../availability"; -import { buildNonDwdCredentials } from "../../domainWideDelegation/clientAndServer"; +import { buildNonDelegationCredentials } from "../../delegationCredential/clientAndServer"; import { withSelectedCalendars } from "../withSelectedCalendars"; import { ProfileRepository } from "./profile"; import { getParsedTeam } from "./teamUtils"; @@ -786,7 +786,7 @@ export class UserRepository { const { credentials, ...userWithSelectedCalendars } = withSelectedCalendars(user); return { ...userWithSelectedCalendars, - credentials: buildNonDwdCredentials(credentials), + credentials: buildNonDelegationCredentials(credentials), }; } diff --git a/packages/platform/atoms/destination-calendar/DestinationCalendarSelector.tsx b/packages/platform/atoms/destination-calendar/DestinationCalendarSelector.tsx index e1c299e194..44ded39b5d 100644 --- a/packages/platform/atoms/destination-calendar/DestinationCalendarSelector.tsx +++ b/packages/platform/atoms/destination-calendar/DestinationCalendarSelector.tsx @@ -14,11 +14,7 @@ import { getPlaceholderContent } from "../lib/getPlaceholderContent"; export type DestinationCalendarProps = { connectedCalendars: ConnectedDestinationCalendars["connectedCalendars"]; destinationCalendar: ConnectedDestinationCalendars["destinationCalendar"]; - onChange: (value: { - externalId: string; - integration: string; - domainWideDelegationCredentialId?: string; - }) => void; + onChange: (value: { externalId: string; integration: string; delegationCredentialId?: string }) => void; isPending?: boolean; hidePlaceholder?: boolean; value: string | undefined; @@ -41,7 +37,7 @@ export const DestinationCalendarSelector = ({ value: string; label: string; subtitle: string; - domainWideDelegationCredentialId?: string; + delegationCredentialId?: string; } | null>(null); useEffect(() => { @@ -82,7 +78,7 @@ export const DestinationCalendarSelector = ({ selectedCalendar?.primary?.name })`, value: `${cal.integration}:${cal.externalId}`, - domainWideDelegationCredentialId: cal.domainWideDelegationCredentialId || undefined, + delegationCredentialId: cal.delegationCredentialId || undefined, })), })) ?? [] ); @@ -141,7 +137,7 @@ export const DestinationCalendarSelector = ({ onChange({ integration, externalId, - domainWideDelegationCredentialId: newValue.domainWideDelegationCredentialId, + delegationCredentialId: newValue.delegationCredentialId, }); }} isLoading={isPending} diff --git a/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx b/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx index 751d931984..ae42a80e1c 100644 --- a/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx @@ -38,11 +38,11 @@ export const DestinationCalendarSettingsPlatformWrapper = ({ value={calendars.data.destinationCalendar.externalId} hidePlaceholder hideAdvancedText - onChange={async ({ externalId, integration, domainWideDelegationCredentialId }) => { + onChange={async ({ externalId, integration, delegationCredentialId }) => { await updateDestinationCalendars({ integration, externalId, - domainWideDelegationCredentialId, + delegationCredentialId, }); }} isPending={isUpdatingCalendar} diff --git a/packages/platform/atoms/hooks/calendars/useAddSelectedCalendar.ts b/packages/platform/atoms/hooks/calendars/useAddSelectedCalendar.ts index 806b7ad050..370a01aa5d 100644 --- a/packages/platform/atoms/hooks/calendars/useAddSelectedCalendar.ts +++ b/packages/platform/atoms/hooks/calendars/useAddSelectedCalendar.ts @@ -35,15 +35,15 @@ export const useAddSelectedCalendar = ( credentialId: number; integration: string; externalId: string; - domainWideDelegationCredentialId: string | null; + delegationCredentialId: string | null; } >({ mutationFn: (data) => { - const { domainWideDelegationCredentialId, ...rest } = data; + const { delegationCredentialId, ...rest } = data; return http .post(`/selected-calendars`, { ...rest, - ...(domainWideDelegationCredentialId ? { domainWideDelegationCredentialId } : {}), + ...(delegationCredentialId ? { delegationCredentialId } : {}), }) .then((res) => { return res.data; diff --git a/packages/platform/atoms/hooks/calendars/useRemoveSelectedCalendar.ts b/packages/platform/atoms/hooks/calendars/useRemoveSelectedCalendar.ts index b30a855026..ce1df62cc0 100644 --- a/packages/platform/atoms/hooks/calendars/useRemoveSelectedCalendar.ts +++ b/packages/platform/atoms/hooks/calendars/useRemoveSelectedCalendar.ts @@ -35,21 +35,17 @@ export const useRemoveSelectedCalendar = ( credentialId: number; integration: string; externalId: string; - domainWideDelegationCredentialId: string | null; + delegationCredentialId: string | null; } >({ mutationFn: (data) => { - const { credentialId, externalId, integration, domainWideDelegationCredentialId } = data; + const { credentialId, externalId, integration, delegationCredentialId } = data; return http .delete( `/selected-calendars?credentialId=${credentialId}&integration=${integration}&externalId=${encodeURIComponent( externalId - )}${ - domainWideDelegationCredentialId - ? `&domainWideDelegationCredentialId=${domainWideDelegationCredentialId}` - : "" - }` + )}${delegationCredentialId ? `&delegationCredentialId=${delegationCredentialId}` : ""}` ) .then((res) => { return res.data; diff --git a/packages/platform/atoms/hooks/calendars/useUpdateDestinationCalendars.ts b/packages/platform/atoms/hooks/calendars/useUpdateDestinationCalendars.ts index ed16c8cc7e..a37c75239f 100644 --- a/packages/platform/atoms/hooks/calendars/useUpdateDestinationCalendars.ts +++ b/packages/platform/atoms/hooks/calendars/useUpdateDestinationCalendars.ts @@ -30,11 +30,11 @@ export const useUpdateDestinationCalendars = ( integration: string; externalId: string; credentialId: number | null; - domainWideDelegationCredentialId?: string; + delegationCredentialId?: string; }; }>, unknown, - { integration: string; externalId: string; domainWideDelegationCredentialId?: string } + { integration: string; externalId: string; delegationCredentialId?: string } >({ mutationFn: (data) => { return http.put(`/destination-calendars`, data).then((res) => { diff --git a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx index 3d87e9a561..0f26e079a3 100644 --- a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx @@ -83,7 +83,7 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ className="border-subtle mt-4 rounded-lg border" actions={
- {allowDelete && !connectedCalendar.domainWideDelegationCredentialId && ( + {allowDelete && !connectedCalendar.delegationCredentialId && ( ); @@ -127,7 +125,7 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ message={{connectedCalendar.error?.message || t("calendar_error")}} iconClassName="h-10 w-10 ml-2 mr-1 mt-0.5" actions={ - !Boolean(connectedCalendar.domainWideDelegationCredentialId) && ( + !Boolean(connectedCalendar.delegationCredentialId) && (
{ - const { isChecked, title, credentialId, type, externalId, domainWideDelegationCredentialId } = props; + const { isChecked, title, credentialId, type, externalId, delegationCredentialId } = props; const [checkedInternal, setCheckedInternal] = useState(isChecked); const { toast } = useToast(); @@ -255,13 +253,13 @@ const PlatformCalendarSwitch = (props: ICalendarSwitchProps) => { externalId: string; }) => { if (isOn) { - await addSelectedCalendar({ credentialId, integration, externalId, domainWideDelegationCredentialId }); + await addSelectedCalendar({ credentialId, integration, externalId, delegationCredentialId }); } else { await removeSelectedCalendar({ credentialId, integration, externalId, - domainWideDelegationCredentialId, + delegationCredentialId, }); } }; diff --git a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx index 896b73cd7c..267211267e 100644 --- a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx +++ b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx @@ -65,8 +65,8 @@ const ConnectedCalendarList = ({ description={connectedCalendar.primary?.email ?? connectedCalendar.integration.description} className="border-subtle mt-4 rounded-lg border" actions={ - // DWD credential can't be disconnected - !connectedCalendar.domainWideDelegationCredentialId && + // Delegation credential can't be disconnected + !connectedCalendar.delegationCredentialId && !disableConnectionModification && (
))} @@ -122,8 +120,8 @@ const ConnectedCalendarList = ({ } iconClassName="h-10 w-10 ml-2 mr-1 mt-0.5" actions={ - // DWD credential can't be disconnected - !connectedCalendar.domainWideDelegationCredentialId && ( + // Delegation credential can't be disconnected + !connectedCalendar.delegationCredentialId && (
app.type == input.appType).map((credential) => credential.id); return { - credentials: [...dwdCredentials, ...credentials], + credentials: [...delegationCredentials, ...credentials], userAdminTeams: userAdminTeamsIds, }; }; diff --git a/packages/trpc/server/routers/loggedInViewer/getUserTopBanners.handler.ts b/packages/trpc/server/routers/loggedInViewer/getUserTopBanners.handler.ts index 41856d4341..dedd658217 100644 --- a/packages/trpc/server/routers/loggedInViewer/getUserTopBanners.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/getUserTopBanners.handler.ts @@ -1,5 +1,5 @@ import { getCalendarCredentials, getConnectedCalendars } from "@calcom/lib/CalendarManager"; -import { buildNonDwdCredentials } from "@calcom/lib/domainWideDelegation/server"; +import { buildNonDelegationCredentials } from "@calcom/lib/delegationCredential/server"; import { prisma } from "@calcom/prisma"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; @@ -24,8 +24,8 @@ const checkInvalidGoogleCalendarCredentials = async ({ ctx }: Props) => { select: credentialForCalendarServiceSelect, }); - // TODO: Call top buildNonDwdCredentials here can be avoided by moving credential prisma query to repository. - const calendarCredentials = getCalendarCredentials(buildNonDwdCredentials(userCredentials)); + // TODO: Call top buildNonDelegationCredentials here can be avoided by moving credential prisma query to repository. + const calendarCredentials = getCalendarCredentials(buildNonDelegationCredentials(userCredentials)); const { connectedCalendars } = await getConnectedCalendars( calendarCredentials, diff --git a/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.test.ts b/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.test.ts index 7f0f991ce5..38a0d78386 100644 --- a/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.test.ts +++ b/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.test.ts @@ -6,7 +6,7 @@ import { getOrganizer, getScenarioData, createOrganization, - createDwdCredential, + createDelegationCredential, } from "@calcom/web/test/utils/bookingScenario/bookingScenario"; import { setupAndTeardown } from "@calcom/web/test/utils/bookingScenario/setupAndTeardown"; @@ -28,7 +28,7 @@ vi.mock("@calcom/lib/CalendarManager", () => ({ describe("setDestinationCalendarHandler", () => { setupAndTeardown(); - it("should successfully set destination calendar with DWD credentials", async () => { + it("should successfully set destination calendar with DelegationCredential credentials", async () => { const org = await createOrganization({ name: "Test Org", slug: "testorg", @@ -73,9 +73,9 @@ describe("setDestinationCalendarHandler", () => { ], }); - const dwd = await createDwdCredential(org.id); + const delegationCredential = await createDelegationCredential(org.id); - const dwdCredentialId = dwd.id; + const delegationCredentialId = delegationCredential.id; const testExternalId = "TEST@group.calendar.google.com"; // Mock the getConnectedCalendars response @@ -90,7 +90,7 @@ describe("setDestinationCalendarHandler", () => { primary: true, email: organizer.email, credentialId: -1, - domainWideDelegationCredentialId: dwdCredentialId, + delegationCredentialId: delegationCredentialId, }, { externalId: testExternalId, @@ -99,7 +99,7 @@ describe("setDestinationCalendarHandler", () => { primary: null, email: organizer.email, credentialId: -1, - domainWideDelegationCredentialId: dwdCredentialId, + delegationCredentialId: delegationCredentialId, }, ], }, @@ -129,7 +129,7 @@ describe("setDestinationCalendarHandler", () => { integration: "google_calendar", externalId: testExternalId, credentialId: null, - domainWideDelegationCredentialId: dwdCredentialId, + delegationCredentialId: delegationCredentialId, }, ], }, @@ -146,7 +146,7 @@ describe("setDestinationCalendarHandler", () => { integration: "google_calendar", externalId: testExternalId, credentialId: null, - domainWideDelegationCredentialId: dwdCredentialId, + delegationCredentialId: delegationCredentialId, }, ], } as NonNullable, @@ -172,7 +172,7 @@ describe("setDestinationCalendarHandler", () => { integration: "google_calendar", externalId: testExternalId, credentialId: null, - domainWideDelegationCredentialId: dwdCredentialId, + delegationCredentialId: delegationCredentialId, }) ); }); diff --git a/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts b/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts index bcecad7017..de0f876ba9 100644 --- a/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/setDestinationCalendar.handler.ts @@ -33,11 +33,11 @@ export const getFirstConnectedCalendar = ({ }) => { const calendars = connectedCalendars.flatMap((c) => c.calendars ?? []); const matchingCalendars = calendars.filter(matcher); - const dwdCredentialCalendar = matchingCalendars.find((cal) => !!cal.domainWideDelegationCredentialId); + const delegationCredentialCalendar = matchingCalendars.find((cal) => !!cal.delegationCredentialId); - // Prefer DWD credential calendar as there could be other one due to existing connections even after DWD is enabled. - if (dwdCredentialCalendar) { - return dwdCredentialCalendar; + // Prefer Delegation credential calendar as there could be other one due to existing connections even after DelegationCredential is enabled. + if (delegationCredentialCalendar) { + return delegationCredentialCalendar; } else { return matchingCalendars[0]; } @@ -48,8 +48,8 @@ export const getFirstConnectedCalendar = ({ * Also, DestinationCalendar doesn't have unique constraint on externalId, integration and eventTypeId, so there could be multiple destinationCalendars with same externalId, integration and eventTypeId in DB. * So, it could update any of the destinationCalendar when there are duplicates in DB. Ideally we should have unique constraint on externalId, integration and eventTypeId. * - * With the addition of DWD credential, it adds another dimension to the problem. - * A user could have DWD and non-DWD credential for the same calendar and he might be selecting DWD credential connected calendar but it could still be set with nullish destinationCalendar.domainWideDelegationCredentialId. + * With the addition of Delegation credential, it adds another dimension to the problem. + * A user could have DelegationCredential and non-Delegation credential for the same calendar and he might be selecting Delegation credential connected calendar but it could still be set with nullish destinationCalendar.delegationCredentialId. */ export const setDestinationCalendarHandler = async ({ ctx, input }: SetDestinationCalendarOptions) => { const { user } = ctx; @@ -69,11 +69,11 @@ export const setDestinationCalendarHandler = async ({ ctx, input }: SetDestinati cal.externalId === externalId && cal.integration === integration && cal.readOnly === false, }); - const { credentialId, domainWideDelegationCredentialId } = firstConnectedCalendar || {}; + const { credentialId, delegationCredentialId } = firstConnectedCalendar || {}; let where; - if (!credentialId && !domainWideDelegationCredentialId) { + if (!credentialId && !delegationCredentialId) { throw new TRPCError({ code: "BAD_REQUEST", message: `Could not find calendar ${input.externalId}` }); } @@ -81,8 +81,7 @@ export const setDestinationCalendarHandler = async ({ ctx, input }: SetDestinati allCals.find( (cal) => cal.primary && - (cal.credentialId === credentialId || - cal.domainWideDelegationCredentialId === domainWideDelegationCredentialId) + (cal.credentialId === credentialId || cal.delegationCredentialId === delegationCredentialId) )?.email ?? null; if (eventTypeId) { @@ -110,7 +109,7 @@ export const setDestinationCalendarHandler = async ({ ctx, input }: SetDestinati externalId, primaryEmail, credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, }, create: { ...where, @@ -118,7 +117,7 @@ export const setDestinationCalendarHandler = async ({ ctx, input }: SetDestinati externalId, primaryEmail, credentialId, - domainWideDelegationCredentialId, + delegationCredentialId, }, }); }; diff --git a/packages/trpc/server/routers/viewer/_router.tsx b/packages/trpc/server/routers/viewer/_router.tsx index f0eaaee1cd..b1fff5d14e 100644 --- a/packages/trpc/server/routers/viewer/_router.tsx +++ b/packages/trpc/server/routers/viewer/_router.tsx @@ -15,8 +15,8 @@ import { attributesRouter } from "./attributes/_router"; import { authRouter } from "./auth/_router"; import { availabilityRouter } from "./availability/_router"; import { bookingsRouter } from "./bookings/_router"; +import { delegationCredentialRouter } from "./delegationCredential/_router"; import { deploymentSetupRouter } from "./deploymentSetup/_router"; -import { domainWideDelegationRouter } from "./domainWideDelegation/_router"; import { dsyncRouter } from "./dsync/_router"; import { eventTypesRouter } from "./eventTypes/_router"; import { googleWorkspaceRouter } from "./googleWorkspace/_router"; @@ -45,7 +45,7 @@ export const viewerRouter = mergeRouters( teams: viewerTeamsRouter, timezones: timezonesRouter, organizations: viewerOrganizationsRouter, - domainWideDelegation: domainWideDelegationRouter, + delegationCredential: delegationCredentialRouter, webhook: webhookRouter, apiKeys: apiKeysRouter, slots: slotsRouter, diff --git a/packages/trpc/server/routers/viewer/apps/queryForDependencies.handler.ts b/packages/trpc/server/routers/viewer/apps/queryForDependencies.handler.ts index cfaa017756..f78f8d991a 100644 --- a/packages/trpc/server/routers/viewer/apps/queryForDependencies.handler.ts +++ b/packages/trpc/server/routers/viewer/apps/queryForDependencies.handler.ts @@ -1,5 +1,5 @@ import { getAppFromSlug } from "@calcom/app-store/utils"; -import { getAllDwdCredentialsForUserByAppSlug } from "@calcom/lib/domainWideDelegation/server"; +import { getAllDelegationCredentialsForUserByAppSlug } from "@calcom/lib/delegationCredential/server"; import { prisma } from "@calcom/prisma"; import type { TrpcSessionUser } from "../../../trpc"; @@ -27,11 +27,11 @@ export const queryForDependenciesHandler = async ({ ctx, input }: QueryForDepend }, }); - const dwdCredentials = await getAllDwdCredentialsForUserByAppSlug({ + const delegationCredentials = await getAllDelegationCredentialsForUserByAppSlug({ user: ctx.user, appSlug: appId, }); - const appInstalled = !!dbCredential || !!dwdCredentials.length; + const appInstalled = !!dbCredential || !!delegationCredentials.length; const app = getAppFromSlug(dependency); diff --git a/packages/trpc/server/routers/viewer/availability/calendarOverlay.handler.ts b/packages/trpc/server/routers/viewer/availability/calendarOverlay.handler.ts index 6fb4f30a01..4eabb84432 100644 --- a/packages/trpc/server/routers/viewer/availability/calendarOverlay.handler.ts +++ b/packages/trpc/server/routers/viewer/availability/calendarOverlay.handler.ts @@ -1,6 +1,6 @@ import dayjs from "@calcom/dayjs"; import { getBusyCalendarTimes } from "@calcom/lib/CalendarManager"; -import { enrichUserWithDwdCredentialsWithoutOrgId } from "@calcom/lib/domainWideDelegation/server"; +import { enrichUserWithDelegationCredentialsWithoutOrgId } from "@calcom/lib/delegationCredential/server"; import { prisma } from "@calcom/prisma"; import type { EventBusyDate } from "@calcom/types/Calendar"; @@ -30,7 +30,7 @@ export const calendarOverlayHandler = async ({ ctx, input }: ListOptions) => { // To call getCalendar we need // Ensure that the user has access to all of the credentialIds - const nonDwdCredentials = await prisma.credential.findMany({ + const nonDelegationCredentials = await prisma.credential.findMany({ where: { id: { in: uniqueCredentialIds, @@ -53,10 +53,10 @@ export const calendarOverlayHandler = async ({ ctx, input }: ListOptions) => { }, }); - const { credentials } = await enrichUserWithDwdCredentialsWithoutOrgId({ + const { credentials } = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: { ...user, - credentials: nonDwdCredentials, + credentials: nonDelegationCredentials, }, }); diff --git a/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts b/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts index 92db3fbf49..0aeb7cc39d 100644 --- a/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts +++ b/packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts @@ -11,7 +11,7 @@ import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload"; import { isPrismaObjOrUndefined } from "@calcom/lib"; import { CalendarEventBuilder } from "@calcom/lib/builders/CalendarEvent/builder"; import { CalendarEventDirector } from "@calcom/lib/builders/CalendarEvent/director"; -import { getDwdOrRegularCredential } from "@calcom/lib/domainWideDelegation/server"; +import { getDelegationCredentialOrRegularCredential } from "@calcom/lib/delegationCredential/server"; import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server"; import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId"; import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType"; @@ -240,22 +240,22 @@ export const requestRescheduleHandler = async ({ ctx, input }: RequestReschedule if (bookingRef.type.endsWith("_calendar")) { const calendar = await getCalendar( - getDwdOrRegularCredential({ + getDelegationCredentialOrRegularCredential({ credentials, id: { credentialId: bookingRef?.credentialId, - dwdId: bookingRef?.domainWideDelegationCredentialId, + delegationCredentialId: bookingRef?.delegationCredentialId, }, }) ); return calendar?.deleteEvent(bookingRef.uid, builder.calendarEvent, bookingRef.externalCalendarId); } else if (bookingRef.type.endsWith("_video")) { return deleteMeeting( - getDwdOrRegularCredential({ + getDelegationCredentialOrRegularCredential({ credentials, id: { credentialId: bookingRef?.credentialId, - dwdId: bookingRef?.domainWideDelegationCredentialId, + delegationCredentialId: bookingRef?.delegationCredentialId, }, }), bookingRef.uid diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/_router.ts b/packages/trpc/server/routers/viewer/delegationCredential/_router.ts similarity index 67% rename from packages/trpc/server/routers/viewer/domainWideDelegation/_router.ts rename to packages/trpc/server/routers/viewer/delegationCredential/_router.ts index 5c0171c4a6..11b6b5c8ec 100644 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/_router.ts +++ b/packages/trpc/server/routers/viewer/delegationCredential/_router.ts @@ -6,16 +6,16 @@ import { TRPCError } from "@trpc/server"; import authedProcedure, { authedOrgAdminProcedure } from "../../../procedures/authedProcedure"; import { router, importHandler } from "../../../trpc"; import { - DomainWideDelegationCreateSchema, - DomainWideDelegationUpdateSchema, - DomainWideDelegationDeleteSchema, - DomainWideDelegationToggleEnabledSchema, + DelegationCredentialCreateSchema, + DelegationCredentialUpdateSchema, + DelegationCredentialDeleteSchema, + DelegationCredentialToggleEnabledSchema, } from "./schema"; -const NAMESPACE = "domainWideDelegation"; +const NAMESPACE = "delegationCredential"; const namespaced = (s: string) => `${NAMESPACE}.${s}`; -const checkDomainWideDelegationFeature = async ({ +const checkDelegationCredentialFeature = async ({ ctx, next, }: { @@ -33,41 +33,41 @@ const checkDomainWideDelegationFeature = async ({ }); } - const hasDomainWideDelegation = await featureRepo.checkIfTeamHasFeature( + const hasDelegationCredential = await featureRepo.checkIfTeamHasFeature( user.organizationId, - "domain-wide-delegation" + "delegation-credential" ); - if (!hasDomainWideDelegation) { + if (!hasDelegationCredential) { throw new TRPCError({ code: "FORBIDDEN", - message: t("domain_wide_delegation_feature_not_enabled"), + message: t("delegation_credential_feature_not_enabled"), }); } return next(); }; -export const domainWideDelegationRouter = router({ - list: authedOrgAdminProcedure.use(checkDomainWideDelegationFeature).query(async (opts) => { +export const delegationCredentialRouter = router({ + list: authedOrgAdminProcedure.use(checkDelegationCredentialFeature).query(async (opts) => { const handler = await importHandler(namespaced("list"), () => import("./list.handler")); return handler(opts); }), update: authedOrgAdminProcedure - .use(checkDomainWideDelegationFeature) - .input(DomainWideDelegationUpdateSchema) + .use(checkDelegationCredentialFeature) + .input(DelegationCredentialUpdateSchema) .mutation(async (opts) => { const handler = await importHandler(namespaced("update"), () => import("./update.handler")); return handler(opts); }), add: authedOrgAdminProcedure - .use(checkDomainWideDelegationFeature) - .input(DomainWideDelegationCreateSchema) + .use(checkDelegationCredentialFeature) + .input(DelegationCredentialCreateSchema) .mutation(async (opts) => { const handler = await importHandler(namespaced("add"), () => import("./add.handler")); return handler(opts); }), toggleEnabled: authedOrgAdminProcedure - .use(checkDomainWideDelegationFeature) - .input(DomainWideDelegationToggleEnabledSchema) + .use(checkDelegationCredentialFeature) + .input(DelegationCredentialToggleEnabledSchema) .mutation(async (opts) => { const handler = await importHandler( namespaced("toggleEnabled"), @@ -76,13 +76,13 @@ export const domainWideDelegationRouter = router({ return handler(opts); }), delete: authedOrgAdminProcedure - .use(checkDomainWideDelegationFeature) - .input(DomainWideDelegationDeleteSchema) + .use(checkDelegationCredentialFeature) + .input(DelegationCredentialDeleteSchema) .mutation(async (opts) => { const handler = await importHandler(namespaced("delete"), () => import("./delete.handler")); return handler(opts); }), - listWorkspacePlatforms: authedProcedure.use(checkDomainWideDelegationFeature).query(async () => { + listWorkspacePlatforms: authedProcedure.use(checkDelegationCredentialFeature).query(async () => { const handler = await importHandler( namespaced("listWorkspacePlatforms"), () => import("./listWorkspacePlatforms.handler") diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/add.handler.ts b/packages/trpc/server/routers/viewer/delegationCredential/add.handler.ts similarity index 68% rename from packages/trpc/server/routers/viewer/domainWideDelegation/add.handler.ts rename to packages/trpc/server/routers/viewer/delegationCredential/add.handler.ts index a5eeb29493..189dda593a 100644 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/add.handler.ts +++ b/packages/trpc/server/routers/viewer/delegationCredential/add.handler.ts @@ -1,22 +1,22 @@ import type { z } from "zod"; -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; import { WorkspacePlatformRepository } from "@calcom/lib/server/repository/workspacePlatform"; import { TRPCError } from "@trpc/server"; -import type { DomainWideDelegationCreateSchema } from "./schema"; +import type { DelegationCredentialCreateSchema } from "./schema"; import { - ensureDomainWideDelegationNotAlreadyConfigured, + ensureDelegationCredentialNotAlreadyConfigured, ensureNoServiceAccountKey, - handleDomainWideDelegationError, + handleDelegationCredentialError, } from "./utils"; export default async function handler({ input, ctx, }: { - input: z.infer; + input: z.infer; ctx: { user: { id: number; organizationId: number | null } }; }) { const { workspacePlatformSlug, domain, serviceAccountKey } = input; @@ -26,7 +26,7 @@ export default async function handler({ if (!organizationId) { throw new TRPCError({ code: "UNAUTHORIZED", - message: "You must be part of an organization to add a domain-wide delegation", + message: "You must be part of an organization to add a delegation credential", }); } @@ -42,13 +42,13 @@ export default async function handler({ }); } - await ensureDomainWideDelegationNotAlreadyConfigured({ + await ensureDelegationCredentialNotAlreadyConfigured({ domain, currentOrganizationId: organizationId, - dwdBeingUpdatedId: null, + delegationCredentialBeingUpdatedId: null, }); - const createdDelegation = await DomainWideDelegationRepository.create({ + const createdDelegation = await DelegationCredentialRepository.create({ workspacePlatformId: workspacePlatform.id, domain, // We don't want to enable by default because enabling requires some checks to be completed and it has a separate flow. @@ -59,6 +59,6 @@ export default async function handler({ return ensureNoServiceAccountKey(createdDelegation); } catch (error) { - handleDomainWideDelegationError(error); + handleDelegationCredentialError(error); } } diff --git a/packages/trpc/server/routers/viewer/delegationCredential/delete.handler.ts b/packages/trpc/server/routers/viewer/delegationCredential/delete.handler.ts new file mode 100644 index 0000000000..b8d7db6beb --- /dev/null +++ b/packages/trpc/server/routers/viewer/delegationCredential/delete.handler.ts @@ -0,0 +1,21 @@ +import type z from "zod"; + +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; + +import { TRPCError } from "@trpc/server"; + +import type { DelegationCredentialDeleteSchema } from "./schema"; + +export default async function handler({ + input, +}: { + input: z.infer; +}) { + const { id } = input; + + // We might want to consider allowing this in the future. Right now, toggling off DelegationCredential achieves similar but non-destructive effect + throw new TRPCError({ code: "BAD_REQUEST", message: "Not allowed" }); + await DelegationCredentialRepository.deleteById({ id }); + + return { id }; +} diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/list.handler.ts b/packages/trpc/server/routers/viewer/delegationCredential/list.handler.ts similarity index 73% rename from packages/trpc/server/routers/viewer/domainWideDelegation/list.handler.ts rename to packages/trpc/server/routers/viewer/delegationCredential/list.handler.ts index 0833140d39..e604e8f238 100644 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/list.handler.ts +++ b/packages/trpc/server/routers/viewer/delegationCredential/list.handler.ts @@ -1,4 +1,4 @@ -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; import { encryptedServiceAccountKeySchema } from "@calcom/lib/server/serviceAccountKey"; import type { PrismaClient } from "@calcom/prisma"; @@ -14,14 +14,14 @@ export default async function handler({ const organizationId = user.organization?.id; if (!organizationId) { - throw new Error("You must be in an organization to list domain wide delegations"); + throw new Error("You must be in an organization to list delegation credentials"); } - const domainWideDelegations = - await DomainWideDelegationRepository.findByOrgIdIncludeSensitiveServiceAccountKey({ + const delegationCredentials = + await DelegationCredentialRepository.findByOrgIdIncludeSensitiveServiceAccountKey({ organizationId, }); - return domainWideDelegations.map((delegation) => { + return delegationCredentials.map((delegation) => { // Let's not parse the service account key here, we should be able to fix the item with the problem, so we always try to return the complete list const serviceAccountKey = encryptedServiceAccountKeySchema.safeParse(delegation.serviceAccountKey); diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/listWorkspacePlatforms.handler.ts b/packages/trpc/server/routers/viewer/delegationCredential/listWorkspacePlatforms.handler.ts similarity index 100% rename from packages/trpc/server/routers/viewer/domainWideDelegation/listWorkspacePlatforms.handler.ts rename to packages/trpc/server/routers/viewer/delegationCredential/listWorkspacePlatforms.handler.ts diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/schema.ts b/packages/trpc/server/routers/viewer/delegationCredential/schema.ts similarity index 59% rename from packages/trpc/server/routers/viewer/domainWideDelegation/schema.ts rename to packages/trpc/server/routers/viewer/delegationCredential/schema.ts index b30c145230..a08d1a3138 100644 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/schema.ts +++ b/packages/trpc/server/routers/viewer/delegationCredential/schema.ts @@ -2,23 +2,23 @@ import { z } from "zod"; import { serviceAccountKeySchema } from "@calcom/prisma/zod-utils"; -export const DomainWideDelegationCreateSchema = z.object({ +export const DelegationCredentialCreateSchema = z.object({ workspacePlatformSlug: z.string(), domain: z.string(), serviceAccountKey: serviceAccountKeySchema, }); -export const DomainWideDelegationUpdateSchema = z.object({ +export const DelegationCredentialUpdateSchema = z.object({ id: z.string(), workspacePlatformSlug: z.string(), domain: z.string(), }); -export const DomainWideDelegationToggleEnabledSchema = z.object({ +export const DelegationCredentialToggleEnabledSchema = z.object({ id: z.string(), enabled: z.boolean(), }); -export const DomainWideDelegationDeleteSchema = z.object({ +export const DelegationCredentialDeleteSchema = z.object({ id: z.string(), }); diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/toggleEnabled.handler.ts b/packages/trpc/server/routers/viewer/delegationCredential/toggleEnabled.handler.ts similarity index 58% rename from packages/trpc/server/routers/viewer/domainWideDelegation/toggleEnabled.handler.ts rename to packages/trpc/server/routers/viewer/delegationCredential/toggleEnabled.handler.ts index 71e82c231c..b5c5f3a9c7 100644 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/toggleEnabled.handler.ts +++ b/packages/trpc/server/routers/viewer/delegationCredential/toggleEnabled.handler.ts @@ -1,11 +1,11 @@ import type { z } from "zod"; -import { checkIfSuccessfullyConfiguredInWorkspace } from "@calcom/lib/domainWideDelegation/server"; +import { checkIfSuccessfullyConfiguredInWorkspace } from "@calcom/lib/delegationCredential/server"; import { getTranslation } from "@calcom/lib/server/i18n"; -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; -import type { ServiceAccountKey } from "@calcom/lib/server/repository/domainWideDelegation"; +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; +import type { ServiceAccountKey } from "@calcom/lib/server/repository/delegationCredential"; -import type { DomainWideDelegationToggleEnabledSchema } from "./schema"; +import type { DelegationCredentialToggleEnabledSchema } from "./schema"; import { ensureNoServiceAccountKey } from "./utils"; type LoggedInUser = { @@ -23,7 +23,7 @@ export default async function toggleEnabledHandler({ ctx: { user: LoggedInUser; }; - input: z.infer; + input: z.infer; }) { const { user: loggedInUser } = ctx; const t = await getTranslation(ctx.user.locale ?? "en", "common"); @@ -32,51 +32,53 @@ export default async function toggleEnabledHandler({ throw new Error(t("verify_your_email")); } - return toggleDwdEnabled(loggedInUser, input); + return toggleDelegationCredentialEnabled(loggedInUser, input); } -export async function toggleDwdEnabled( +export async function toggleDelegationCredentialEnabled( loggedInUser: Omit, - input: z.infer + input: z.infer ) { if (input.enabled) { await assertWorkspaceConfigured({ - domainWideDelegationId: input.id, + delegationCredentialId: input.id, user: loggedInUser, }); } - const updatedDomainWideDelegation = await DomainWideDelegationRepository.updateById({ + const updatedDelegationCredential = await DelegationCredentialRepository.updateById({ id: input.id, data: { enabled: input.enabled, }, }); - return ensureNoServiceAccountKey(updatedDomainWideDelegation); + return ensureNoServiceAccountKey(updatedDelegationCredential); } const assertWorkspaceConfigured = async ({ - domainWideDelegationId, + delegationCredentialId, user, }: { - domainWideDelegationId: string; + delegationCredentialId: string; user: { id: number; email: string; organizationId: number | null }; }) => { - const dwd = await DomainWideDelegationRepository.findByIdIncludeSensitiveServiceAccountKey({ - id: domainWideDelegationId, - }); + const delegationCredential = await DelegationCredentialRepository.findByIdIncludeSensitiveServiceAccountKey( + { + id: delegationCredentialId, + } + ); - if (!dwd) { + if (!delegationCredential) { throw new Error("Domain wide delegation not found"); } - if (!hasServiceAccountKey(dwd)) { + if (!hasServiceAccountKey(delegationCredential)) { throw new Error("Domain wide delegation doesn't have service account key"); } const isSuccessfullyConfigured = await checkIfSuccessfullyConfiguredInWorkspace({ - dwd, + delegationCredential, user, }); @@ -86,7 +88,7 @@ const assertWorkspaceConfigured = async ({ }; function hasServiceAccountKey( - domainWideDelegation: T -): domainWideDelegation is T & { serviceAccountKey: ServiceAccountKey } { - return domainWideDelegation.serviceAccountKey !== null; + delegationCredential: T +): delegationCredential is T & { serviceAccountKey: ServiceAccountKey } { + return delegationCredential.serviceAccountKey !== null; } diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/update.handler.ts b/packages/trpc/server/routers/viewer/delegationCredential/update.handler.ts similarity index 69% rename from packages/trpc/server/routers/viewer/domainWideDelegation/update.handler.ts rename to packages/trpc/server/routers/viewer/delegationCredential/update.handler.ts index b07fe1abf4..0ba2194779 100644 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/update.handler.ts +++ b/packages/trpc/server/routers/viewer/delegationCredential/update.handler.ts @@ -1,22 +1,22 @@ import type { z } from "zod"; -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; import { WorkspacePlatformRepository } from "@calcom/lib/server/repository/workspacePlatform"; import { TRPCError } from "@trpc/server"; -import type { DomainWideDelegationUpdateSchema } from "./schema"; +import type { DelegationCredentialUpdateSchema } from "./schema"; import { - ensureDomainWideDelegationNotAlreadyConfigured, + ensureDelegationCredentialNotAlreadyConfigured, ensureNoServiceAccountKey, - handleDomainWideDelegationError, + handleDelegationCredentialError, } from "./utils"; export default async function handler({ input, ctx, }: { - input: z.infer; + input: z.infer; ctx: { user: { id: number; organizationId: number | null } }; }) { const { id, workspacePlatformSlug, domain } = input; @@ -26,14 +26,14 @@ export default async function handler({ if (!organizationId) { throw new TRPCError({ code: "UNAUTHORIZED", - message: "You must be part of an organization to update a domain wide delegation", + message: "You must be part of an organization to update a delegation credential", }); } - await ensureDomainWideDelegationNotAlreadyConfigured({ + await ensureDelegationCredentialNotAlreadyConfigured({ domain, currentOrganizationId: organizationId, - dwdBeingUpdatedId: id, + delegationCredentialBeingUpdatedId: id, }); try { @@ -48,7 +48,7 @@ export default async function handler({ }); } - const updatedDelegation = await DomainWideDelegationRepository.updateById({ + const updatedDelegation = await DelegationCredentialRepository.updateById({ id, data: { workspacePlatformId: workspacePlatform.id, @@ -59,6 +59,6 @@ export default async function handler({ return ensureNoServiceAccountKey(updatedDelegation); } catch (error) { - handleDomainWideDelegationError(error); + handleDelegationCredentialError(error); } } diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/utils.ts b/packages/trpc/server/routers/viewer/delegationCredential/utils.ts similarity index 66% rename from packages/trpc/server/routers/viewer/domainWideDelegation/utils.ts rename to packages/trpc/server/routers/viewer/delegationCredential/utils.ts index f0677b59d2..475bc249c8 100644 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/utils.ts +++ b/packages/trpc/server/routers/viewer/delegationCredential/utils.ts @@ -2,12 +2,12 @@ import { z } from "zod"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; +import { DelegationCredentialRepository } from "@calcom/lib/server/repository/delegationCredential"; import type { Prisma } from "@calcom/prisma/client"; import { TRPCError } from "@trpc/server"; -const log = logger.getSubLogger({ prefix: ["domainWideDelegation/utils"] }); +const log = logger.getSubLogger({ prefix: ["delegationCredential/utils"] }); export class InvalidServiceAccountKeyError extends Error { constructor(message: string) { super(message); @@ -15,27 +15,29 @@ export class InvalidServiceAccountKeyError extends Error { } } -export async function ensureDomainWideDelegationNotAlreadyConfigured({ +export async function ensureDelegationCredentialNotAlreadyConfigured({ domain, currentOrganizationId, - dwdBeingUpdatedId, + delegationCredentialBeingUpdatedId, }: { domain: string; currentOrganizationId: number; - dwdBeingUpdatedId: string | null; + delegationCredentialBeingUpdatedId: string | null; }) { - const allDelegationsForDomain = await DomainWideDelegationRepository.findAllByDomain({ + const allDelegationsForDomain = await DelegationCredentialRepository.findAllByDomain({ domain, }); const conflictingDelegationInCurrentOrg = allDelegationsForDomain.find( - (delegation) => delegation.organizationId === currentOrganizationId && delegation.id !== dwdBeingUpdatedId + (delegation) => + delegation.organizationId === currentOrganizationId && + delegation.id !== delegationCredentialBeingUpdatedId ); if (conflictingDelegationInCurrentOrg) { throw new TRPCError({ code: "BAD_REQUEST", - message: `Your organization already has domain-wide delegation for ${conflictingDelegationInCurrentOrg.domain}`, + message: `Your organization already has delegation credential for ${conflictingDelegationInCurrentOrg.domain}`, }); } @@ -46,7 +48,7 @@ export async function ensureDomainWideDelegationNotAlreadyConfigured({ if (differentOrgEnabledDelegations.length > 0) { throw new TRPCError({ code: "BAD_REQUEST", - message: `Domain ${domain} already has domain-wide delegation enabled in another organization`, + message: `Domain ${domain} already has delegation credential enabled in another organization`, }); } } @@ -73,7 +75,7 @@ export function parseServiceAccountKey(serviceAccountKey: Prisma.JsonValue) { return parsedServiceAccountKey.data; } -export const handleDomainWideDelegationError = (error: unknown) => { +export const handleDelegationCredentialError = (error: unknown) => { if (error instanceof InvalidServiceAccountKeyError) { throw new TRPCError({ code: "BAD_REQUEST", @@ -85,20 +87,20 @@ export const handleDomainWideDelegationError = (error: unknown) => { throw error; } - log.error("Error handling domain-wide delegation:", safeStringify(error)); + log.error("Error handling delegation credential:", safeStringify(error)); throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", - message: "An error occurred while handling domain-wide delegation settings.", + message: "An error occurred while handling delegation credential settings.", }); }; export const ensureNoServiceAccountKey = ( - domainWideDelegation: T + delegationCredential: T ) => { - if (!domainWideDelegation) { + if (!delegationCredential) { return null; } - const { serviceAccountKey: _1, ...rest } = domainWideDelegation; + const { serviceAccountKey: _1, ...rest } = delegationCredential; return { ...rest, serviceAccountKey: undefined, diff --git a/packages/trpc/server/routers/viewer/domainWideDelegation/delete.handler.ts b/packages/trpc/server/routers/viewer/domainWideDelegation/delete.handler.ts deleted file mode 100644 index 58df0e3da6..0000000000 --- a/packages/trpc/server/routers/viewer/domainWideDelegation/delete.handler.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type z from "zod"; - -import { DomainWideDelegationRepository } from "@calcom/lib/server/repository/domainWideDelegation"; - -import { TRPCError } from "@trpc/server"; - -import type { DomainWideDelegationDeleteSchema } from "./schema"; - -export default async function handler({ - input, -}: { - input: z.infer; -}) { - const { id } = input; - - // We might want to consider allowing this in the future. Right now, toggling off DWD achieves similar but non-destructive effect - throw new TRPCError({ code: "BAD_REQUEST", message: "Not allowed" }); - await DomainWideDelegationRepository.deleteById({ id }); - - return { id }; -} diff --git a/packages/trpc/server/routers/viewer/organizations/list.handler.ts b/packages/trpc/server/routers/viewer/organizations/list.handler.ts index 541809e7a1..066f7d6392 100644 --- a/packages/trpc/server/routers/viewer/organizations/list.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/list.handler.ts @@ -27,15 +27,15 @@ export const listHandler = async ({ ctx }: ListHandlerInput) => { } const featureRepo = new FeaturesRepository(); - const hasDomainWideDelegation = await featureRepo.checkIfTeamHasFeature( + const hasDelegationCredential = await featureRepo.checkIfTeamHasFeature( organizationId, - "domain-wide-delegation" + "delegation-credential" ); return { ...currentOrg, features: { - domainWideDelegation: hasDomainWideDelegation, + delegationCredential: hasDelegationCredential, }, }; }; diff --git a/packages/trpc/server/routers/viewer/routing-forms/findTeamMembersMatchingAttributeLogicOfRoute.handler.ts b/packages/trpc/server/routers/viewer/routing-forms/findTeamMembersMatchingAttributeLogicOfRoute.handler.ts index 2b9a9612ea..76181bdbc6 100644 --- a/packages/trpc/server/routers/viewer/routing-forms/findTeamMembersMatchingAttributeLogicOfRoute.handler.ts +++ b/packages/trpc/server/routers/viewer/routing-forms/findTeamMembersMatchingAttributeLogicOfRoute.handler.ts @@ -9,7 +9,7 @@ import type { NextApiResponse } from "next"; import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData"; import { getUrlSearchParamsToForwardForTestPreview } from "@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward"; -import { enrichHostsWithDwdCredentials } from "@calcom/lib/domainWideDelegation/server"; +import { enrichHostsWithDelegationCredentials } from "@calcom/lib/delegationCredential/server"; import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils"; import { fromEntriesWithDuplicateKeys } from "@calcom/lib/fromEntriesWithDuplicateKeys"; import { findTeamMembersMatchingAttributeLogic } from "@calcom/lib/raqb/findTeamMembersMatchingAttributeLogic"; @@ -256,7 +256,7 @@ export const findTeamMembersMatchingAttributeLogicOfRouteHandler = async ({ const matchingTeamMembersIds = matchingTeamMembersWithResult.map((member) => member.userId); const matchingTeamMembers = await UserRepository.findByIds({ ids: matchingTeamMembersIds }); - const matchingHosts = await enrichHostsWithDwdCredentials({ + const matchingHosts = await enrichHostsWithDelegationCredentials({ orgId: formOrgId, hosts: eventType.hosts.filter((host) => matchingTeamMembersIds.includes(host.user.id)), }); diff --git a/packages/trpc/server/routers/viewer/slots/util.ts b/packages/trpc/server/routers/viewer/slots/util.ts index 449877b8a3..808ef4300b 100644 --- a/packages/trpc/server/routers/viewer/slots/util.ts +++ b/packages/trpc/server/routers/viewer/slots/util.ts @@ -10,7 +10,7 @@ import { checkForConflicts } from "@calcom/features/bookings/lib/conflictChecker import { isEventTypeLoggingEnabled } from "@calcom/features/bookings/lib/isEventTypeLoggingEnabled"; import { getShouldServeCache } from "@calcom/features/calendar-cache/lib/getShouldServeCache"; import { parseBookingLimit, parseDurationLimit } from "@calcom/lib"; -import { findQualifiedHostsWithDwdCredentials } from "@calcom/lib/bookings/findQualifiedHostsWithDwdCredentials"; +import { findQualifiedHostsWithDelegationCredentials } from "@calcom/lib/bookings/findQualifiedHostsWithDelegationCredentials"; import { shouldIgnoreContactOwner } from "@calcom/lib/bookings/routing/utils"; import { RESERVED_SUBDOMAINS } from "@calcom/lib/constants"; import { getUTCOffsetByTimezone } from "@calcom/lib/date-fns"; @@ -44,10 +44,10 @@ import type { TGetScheduleInputSchema } from "./getSchedule.schema"; import { handleNotificationWhenNoSlots } from "./handleNotificationWhenNoSlots"; const log = logger.getSubLogger({ prefix: ["[slots/util]"] }); -type GetAvailabilityUserWithoutDwdCredentials = Omit & { +type GetAvailabilityUserWithoutDelegationCredentials = Omit & { credentials: CredentialPayload[]; }; -type GetAvailabilityUserWithDwdCredentials = Omit & { +type GetAvailabilityUserWithDelegationCredentials = Omit & { credentials: CredentialForCalendarService[]; }; const selectSelectedSlots = Prisma.validator()({ @@ -272,7 +272,7 @@ export function getUsersWithCredentials({ }: { hosts: { isFixed?: boolean; - user: GetAvailabilityUserWithDwdCredentials; + user: GetAvailabilityUserWithDelegationCredentials; }[]; }) { return hosts.map(({ isFixed, user }) => ({ isFixed, ...user })); @@ -381,7 +381,7 @@ async function _getAvailableSlots({ input, ctx }: GetScheduleOptions): Promise, + findQualifiedHostsWithDelegationCredentials, { eventType, rescheduleUid: input.rescheduleUid ?? null, @@ -956,7 +956,7 @@ const calculateHostsAndAvailabilities = async ({ eventType: Exclude>, null>; hosts: { isFixed?: boolean; - user: GetAvailabilityUserWithDwdCredentials; + user: GetAvailabilityUserWithDelegationCredentials; }[]; loggerWithEventDetails: Logger; startTime: ReturnType; diff --git a/packages/trpc/server/routers/viewer/teams/getMemberAvailability.handler.ts b/packages/trpc/server/routers/viewer/teams/getMemberAvailability.handler.ts index d10b6a7a71..2b87314296 100644 --- a/packages/trpc/server/routers/viewer/teams/getMemberAvailability.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/getMemberAvailability.handler.ts @@ -1,4 +1,4 @@ -import { enrichUserWithDwdCredentialsWithoutOrgId } from "@calcom/lib/domainWideDelegation/server"; +import { enrichUserWithDelegationCredentialsWithoutOrgId } from "@calcom/lib/delegationCredential/server"; import { getUserAvailability } from "@calcom/lib/getUserAvailability"; import { isTeamMember } from "@calcom/lib/server/queries/teams"; import { MembershipRepository } from "@calcom/lib/server/repository/membership"; @@ -27,7 +27,7 @@ export const getMemberAvailabilityHandler = async ({ ctx, input }: GetMemberAvai if (!member.user.username) throw new TRPCError({ code: "BAD_REQUEST", message: "Member doesn't have a username" }); const username = member.user.username; - const user = await enrichUserWithDwdCredentialsWithoutOrgId({ + const user = await enrichUserWithDelegationCredentialsWithoutOrgId({ user: member.user, }); diff --git a/packages/trpc/server/routers/viewer/teams/roundRobin/getRoundRobinHostsToReasign.handler.ts b/packages/trpc/server/routers/viewer/teams/roundRobin/getRoundRobinHostsToReasign.handler.ts index fe6031533f..8783df6794 100644 --- a/packages/trpc/server/routers/viewer/teams/roundRobin/getRoundRobinHostsToReasign.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/roundRobin/getRoundRobinHostsToReasign.handler.ts @@ -1,7 +1,7 @@ import dayjs from "@calcom/dayjs"; import { ensureAvailableUsers } from "@calcom/features/bookings/lib/handleNewBooking/ensureAvailableUsers"; import type { IsFixedAwareUser } from "@calcom/features/bookings/lib/handleNewBooking/types"; -import { enrichUsersWithDwdCredentials } from "@calcom/lib/domainWideDelegation/server"; +import { enrichUsersWithDelegationCredentials } from "@calcom/lib/delegationCredential/server"; import { ErrorCode } from "@calcom/lib/errorCodes"; import logger from "@calcom/lib/logger"; import { withSelectedCalendars } from "@calcom/lib/server/repository/user"; @@ -87,7 +87,7 @@ async function getTeamHostsFromDB({ })); return { - hosts: await enrichUsersWithDwdCredentials({ + hosts: await enrichUsersWithDelegationCredentials({ orgId: organizationId, users: hostsMergedWithUser, }), diff --git a/packages/types/App.d.ts b/packages/types/App.d.ts index 1ea589feb3..5f6fc14f93 100644 --- a/packages/types/App.d.ts +++ b/packages/types/App.d.ts @@ -168,9 +168,9 @@ export interface App { /** Specifies if the App uses an OAuth flow */ isOAuth?: boolean; /** - * Specifies if the App supports domain-wide delegation + * Specifies if the App supports delegation credential */ - domainWideDelegation?: { + delegationCredential?: { workspacePlatformSlug: string; }; } diff --git a/packages/types/Calendar.d.ts b/packages/types/Calendar.d.ts index a1de5f41ca..280c821da4 100644 --- a/packages/types/Calendar.d.ts +++ b/packages/types/Calendar.d.ts @@ -218,7 +218,7 @@ export interface CalendarEvent { platformCancelUrl?: string | null; platformBookingUrl?: string | null; oneTimePassword?: string | null; - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; } export interface EntryPoint { @@ -287,7 +287,7 @@ export interface Calendar { listCalendars(event?: CalendarEvent): Promise; - testDomainWideDelegationSetup?(): Promise; + testDelegationCredentialSetup?(): Promise; watchCalendar?(options: { calendarId: string; diff --git a/packages/types/EventManager.d.ts b/packages/types/EventManager.d.ts index db08f4e2b9..81499a2fdc 100644 --- a/packages/types/EventManager.d.ts +++ b/packages/types/EventManager.d.ts @@ -10,7 +10,7 @@ export interface PartialReference { meetingUrl?: string | null; externalCalendarId?: string | null; credentialId?: number | null; - domainWideDelegationCredentialId?: string | null; + delegationCredentialId?: string | null; } export interface EventResult {