chore: rename DWD to DelegationCredential (#19703)

* chore: rename DWD to DelegationCredential

* fixup! chore: rename DWD to DelegationCredential

* fixup! fixup! chore: rename DWD to DelegationCredential

* fixup! Merge branch 'main' into rename-domain-wide-delegation

* fixup! fixup! Merge branch 'main' into rename-domain-wide-delegation

* fixup! fixup! fixup! Merge branch 'main' into rename-domain-wide-delegation
This commit is contained in:
Morgan
2025-03-04 09:45:39 -03:00
committed by GitHub
parent a39b5e47a7
commit 7a9ddf2194
167 changed files with 4232 additions and 2356 deletions
@@ -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 },
});
@@ -29,12 +29,12 @@ export class DestinationCalendarsController {
@Body() input: DestinationCalendarsInputBodyDto,
@GetUser() user: UserWithProfile
): Promise<DestinationCalendarsOutputResponseDto> {
const { integration, externalId, domainWideDelegationCredentialId } = input;
const { integration, externalId, delegationCredentialId } = input;
const updatedDestinationCalendar = await this.destinationCalendarsService.updateDestinationCalendars(
integration,
externalId,
user.id,
domainWideDelegationCredentialId
delegationCredentialId
);
return {
@@ -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,
},
@@ -37,5 +37,5 @@ export class DestinationCalendarsInputBodyDto {
@IsString()
@IsOptional()
@ApiPropertyOptional()
readonly domainWideDelegationCredentialId?: string;
readonly delegationCredentialId?: string;
}
@@ -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 {
@@ -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()
@@ -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()
@@ -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<CreateDwdOutput> {
const dwd = await this.dwdService.createDwd(orgId, dwdServiceAccountUser, body);
@GetUser() delegatedServiceAccountUser: User,
@Body() body: CreateDelegationCredentialInput
): Promise<CreateDelegationCredentialOutput> {
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<UpdateDwdOutput> {
const dwd = await this.dwdService.updateDwd(orgId, credentialId, dwdServiceAccountUser, body);
): Promise<UpdateDelegationCredentialOutput> {
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" }),
};
}
}
@@ -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 {}
@@ -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 },
});
}
}
@@ -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;
}
@@ -14,7 +14,7 @@ class WorkspacePlatformDto {
slug!: string;
}
export class DwdOutput {
export class DelegationCredentialOutput {
@ApiProperty()
@IsString()
@Expose()
@@ -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;
}
@@ -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;
}
}
@@ -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 {}
@@ -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 },
});
}
}
@@ -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;
}
}
@@ -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,
],
@@ -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;
}
@@ -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,
],
@@ -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,
},
});
@@ -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;