chore: ensure default calendars for delegated users (#20300)

* chore: ensure default calendars for delegated users

* fixup! chore: ensure default calendars for delegated users
This commit is contained in:
Morgan
2025-03-21 15:41:04 +00:00
committed by GitHub
parent c0ecfcd910
commit 15cb687307
4 changed files with 110 additions and 4 deletions
@@ -0,0 +1,33 @@
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
import { Process, Processor } from "@nestjs/bull";
import { Logger } from "@nestjs/common";
import { Job } from "bull";
export const DEFAULT_CALENDARS_JOB = "default_calendars_job";
export const CALENDARS_QUEUE = "calendars";
export type DefaultCalendarsJobDataType = {
userId: number;
};
@Processor(CALENDARS_QUEUE)
export class CalendarsProcessor {
private readonly logger = new Logger(CalendarsProcessor.name);
constructor(public readonly calendarsService: CalendarsService) {}
@Process(DEFAULT_CALENDARS_JOB)
async handleEnsureDefaultCalendars(job: Job<DefaultCalendarsJobDataType>) {
const { userId } = job.data;
try {
// getCalendars calls getConnectedDestinationCalendarsAndEnsureDefaultsInDb from platform libraries
// which gets the calendars from third party providers and ensure default calendars are set in DB
await this.calendarsService.getCalendars(userId);
} catch (err) {
this.logger.error(`Failed to load default calendars of user with id: ${userId}`, {
userId,
err,
});
}
return;
}
}
@@ -1,3 +1,5 @@
import { CalendarsModule } from "@/ee/calendars/calendars.module";
import { CALENDARS_QUEUE, CalendarsProcessor } from "@/ee/calendars/processors/calendars.processor";
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";
@@ -5,16 +7,31 @@ import { OrganizationsRepository } from "@/modules/organizations/index/organizat
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { RedisModule } from "@/modules/redis/redis.module";
import { StripeModule } from "@/modules/stripe/stripe.module";
import { BullModule } from "@nestjs/bull";
import { Module } from "@nestjs/common";
import { OrganizationsDelegationCredentialService } from "./services/organizations-delegation-credential.service";
@Module({
imports: [PrismaModule, StripeModule, RedisModule, MembershipsModule],
imports: [
PrismaModule,
StripeModule,
RedisModule,
CalendarsModule,
MembershipsModule,
BullModule.registerQueue({
name: CALENDARS_QUEUE,
limiter: {
max: 1,
duration: 1000,
},
}),
],
providers: [
OrganizationsDelegationCredentialService,
OrganizationsDelegationCredentialRepository,
OrganizationsRepository,
CalendarsProcessor,
],
controllers: [OrganizationsDelegationCredentialController],
})
@@ -28,4 +28,16 @@ export class OrganizationsDelegationCredentialRepository {
include: { workspacePlatform: true },
});
}
async findDelegatedUserProfiles(orgId: number, domain: string) {
return this.dbRead.prisma.profile.findMany({
select: {
userId: true,
},
where: {
organizationId: orgId,
user: { email: { endsWith: domain } },
},
});
}
}
@@ -1,3 +1,8 @@
import {
CALENDARS_QUEUE,
DEFAULT_CALENDARS_JOB,
DefaultCalendarsJobDataType,
} from "@/ee/calendars/processors/calendars.processor";
import { CreateDelegationCredentialInput } from "@/modules/organizations/delegation-credentials/inputs/create-delegation-credential.input";
import {
GoogleServiceAccountKeyInput,
@@ -5,9 +10,10 @@ import {
} 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 { InjectQueue } from "@nestjs/bull";
import { Injectable, NotFoundException, Logger } from "@nestjs/common";
import { User } from "@prisma/client";
import { Queue } from "bull";
import { encryptServiceAccountKey } from "@calcom/platform-libraries";
import {
@@ -17,8 +23,11 @@ import {
@Injectable()
export class OrganizationsDelegationCredentialService {
private logger = new Logger("OrganizationsDelegationCredentialService");
constructor(
private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository
private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository,
@InjectQueue(CALENDARS_QUEUE) private readonly calendarsQueue: Queue
) {}
async createDelegationCredential(
orgId: number,
@@ -59,9 +68,44 @@ export class OrganizationsDelegationCredentialService {
await this.updateDelegationCredentialServiceAccountKey(delegationCredentialId, body.serviceAccountKey);
}
// once delegation credentials are enabled, slowly set all the destination calendars of delegated users
if (body.enabled === true && delegationCredential.enabled === false) {
await this.ensureDefaultCalendars(orgId, delegationCredential.domain);
}
return { ...delegationCredential, enabled: body?.enabled ?? delegationCredential?.enabled };
}
async ensureDefaultCalendars(orgId: number, domain: string) {
try {
const delegatedUserProfiles =
await this.organizationsDelegationCredentialRepository.findDelegatedUserProfiles(orgId, domain);
delegatedUserProfiles.forEach(async (profile) => {
if (profile.userId) {
const job = await this.calendarsQueue.getJob(`${DEFAULT_CALENDARS_JOB}_${profile.userId}`);
if (job) {
await job.remove();
this.logger.log(`Removed default calendar job for user with id: ${profile.userId}`);
}
this.logger.log(`Adding default calendar job for user with id: ${profile.userId}`);
await this.calendarsQueue.add(
DEFAULT_CALENDARS_JOB,
{
userId: profile.userId,
} satisfies DefaultCalendarsJobDataType,
{ jobId: `${DEFAULT_CALENDARS_JOB}_${profile.userId}`, removeOnComplete: true }
);
}
});
} catch (err) {
this.logger.error(
err,
`Could not ensure default calendars for delegated users in org with id:${orgId}`
);
}
}
async updateDelegationCredentialEnabled(
orgId: number,
delegationCredentialId: string,