better project deletion cleanup

This commit is contained in:
Ramnique Singh 2025-08-18 16:45:10 +05:30
parent 376e308186
commit 29e4a8fb9e
11 changed files with 113 additions and 4 deletions

View file

@ -88,4 +88,12 @@ export interface IComposioTriggerDeploymentsRepository {
* @returns Promise resolving to the number of records deleted
*/
deleteByConnectedAccountId(connectedAccountId: string): Promise<number>;
/**
* Deletes all trigger deployments associated with a specific project.
*
* @param projectId - The unique identifier of the project
* @returns Promise resolving to void
*/
deleteByProjectId(projectId: string): Promise<void>;
}

View file

@ -37,4 +37,12 @@ export interface IConversationsRepository {
// add turn data to conversation
// returns the created turn
addTurn(conversationId: string, data: z.infer<typeof AddTurnData>): Promise<z.infer<typeof Turn>>;
/**
* Deletes all conversations associated with a specific project.
*
* @param projectId - The unique identifier of the project
* @returns Promise resolving to void
*/
deleteByProjectId(projectId: string): Promise<void>;
}

View file

@ -139,4 +139,12 @@ export interface IJobsRepository {
cursor?: string,
limit?: number
): Promise<z.infer<ReturnType<typeof PaginatedList<typeof ListedJobItem>>>>;
/**
* Deletes all jobs associated with a specific project.
*
* @param projectId - The unique identifier of the project
* @returns Promise resolving to void
*/
deleteByProjectId(projectId: string): Promise<void>;
}

View file

@ -89,4 +89,12 @@ export interface IRecurringJobRulesRepository {
* @returns Promise resolving to true if the rule was deleted, false if not found
*/
delete(id: string): Promise<boolean>;
/**
* Deletes all recurring job rules associated with a specific project.
*
* @param projectId - The unique identifier of the project
* @returns Promise resolving to void
*/
deleteByProjectId(projectId: string): Promise<void>;
}

View file

@ -95,4 +95,12 @@ export interface IScheduledJobRulesRepository {
* @returns Promise resolving to true if the rule was deleted, false if not found
*/
delete(id: string): Promise<boolean>;
/**
* Deletes all scheduled job rules associated with a specific project.
*
* @param projectId - The unique identifier of the project
* @returns Promise resolving to void
*/
deleteByProjectId(projectId: string): Promise<void>;
}

View file

@ -6,6 +6,13 @@ import { IApiKeysRepository } from "../../repositories/api-keys.repository.inter
import { IDataSourceDocsRepository } from "../../repositories/data-source-docs.repository.interface";
import { IDataSourcesRepository } from "../../repositories/data-sources.repository.interface";
import { qdrantClient } from "@/app/lib/qdrant";
import { IComposioTriggerDeploymentsRepository } from "../../repositories/composio-trigger-deployments.repository.interface";
import { IConversationsRepository } from "../../repositories/conversations.repository.interface";
import { IJobsRepository } from "../../repositories/jobs.repository.interface";
import { IRecurringJobRulesRepository } from "../../repositories/recurring-job-rules.repository.interface";
import { IScheduledJobRulesRepository } from "../../repositories/scheduled-job-rules.repository.interface";
import { NotFoundError } from "@/src/entities/errors/common";
import { deleteConnectedAccount } from "../../lib/composio/composio";
export const InputSchema = z.object({
projectId: z.string(),
@ -25,14 +32,24 @@ export class DeleteProjectUseCase implements IDeleteProjectUseCase {
private readonly apiKeysRepository: IApiKeysRepository;
private readonly dataSourceDocsRepository: IDataSourceDocsRepository;
private readonly dataSourcesRepository: IDataSourcesRepository;
private readonly composioTriggerDeploymentsRepository: IComposioTriggerDeploymentsRepository;
private readonly conversationsRepository: IConversationsRepository;
private readonly jobsRepository: IJobsRepository;
private readonly recurringJobRulesRepository: IRecurringJobRulesRepository;
private readonly scheduledJobRulesRepository: IScheduledJobRulesRepository;
constructor({ projectsRepository, projectMembersRepository, projectActionAuthorizationPolicy, apiKeysRepository, dataSourceDocsRepository, dataSourcesRepository}: {
constructor({ projectsRepository, projectMembersRepository, projectActionAuthorizationPolicy, apiKeysRepository, dataSourceDocsRepository, dataSourcesRepository, composioTriggerDeploymentsRepository, conversationsRepository, jobsRepository, recurringJobRulesRepository, scheduledJobRulesRepository }: {
projectsRepository: IProjectsRepository,
projectMembersRepository: IProjectMembersRepository,
projectActionAuthorizationPolicy: IProjectActionAuthorizationPolicy,
apiKeysRepository: IApiKeysRepository,
dataSourceDocsRepository: IDataSourceDocsRepository,
dataSourcesRepository: IDataSourcesRepository,
composioTriggerDeploymentsRepository: IComposioTriggerDeploymentsRepository,
conversationsRepository: IConversationsRepository,
jobsRepository: IJobsRepository,
recurringJobRulesRepository: IRecurringJobRulesRepository,
scheduledJobRulesRepository: IScheduledJobRulesRepository,
}) {
this.projectsRepository = projectsRepository;
this.projectMembersRepository = projectMembersRepository;
@ -40,6 +57,11 @@ export class DeleteProjectUseCase implements IDeleteProjectUseCase {
this.apiKeysRepository = apiKeysRepository;
this.dataSourceDocsRepository = dataSourceDocsRepository;
this.dataSourcesRepository = dataSourcesRepository;
this.composioTriggerDeploymentsRepository = composioTriggerDeploymentsRepository;
this.conversationsRepository = conversationsRepository;
this.jobsRepository = jobsRepository;
this.recurringJobRulesRepository = recurringJobRulesRepository;
this.scheduledJobRulesRepository = scheduledJobRulesRepository;
}
async execute(request: z.infer<typeof InputSchema>): Promise<void> {
@ -51,12 +73,39 @@ export class DeleteProjectUseCase implements IDeleteProjectUseCase {
projectId,
});
const project = await this.projectsRepository.fetch(projectId);
if (!project) {
throw new NotFoundError('Project not found');
}
// delete connected accounts
await Promise.all(
Object.values(project.composioConnectedAccounts || {}).map(account =>
deleteConnectedAccount(account.id)
)
);
// delete memberships
await this.projectMembersRepository.deleteByProjectId(projectId);
// delete api keys
await this.apiKeysRepository.deleteAll(projectId);
// delete composio trigger deployments
await this.composioTriggerDeploymentsRepository.deleteByProjectId(projectId);
// delete conversations
await this.conversationsRepository.deleteByProjectId(projectId);
// delete jobs
await this.jobsRepository.deleteByProjectId(projectId);
// delete recurring job rules
await this.recurringJobRulesRepository.deleteByProjectId(projectId);
// delete scheduled job rules
await this.scheduledJobRulesRepository.deleteByProjectId(projectId);
// delete data sources data
await this.dataSourceDocsRepository.deleteByProjectId(projectId);
await this.dataSourcesRepository.deleteByProjectId(projectId);
@ -68,9 +117,6 @@ export class DeleteProjectUseCase implements IDeleteProjectUseCase {
},
});
// delete project members
await this.projectMembersRepository.deleteByProjectId(projectId);
// delete project
await this.projectsRepository.delete(projectId);
}

View file

@ -172,4 +172,11 @@ export class MongodbComposioTriggerDeploymentsRepository implements IComposioTri
return result.deletedCount;
}
/**
* Deletes all trigger deployments associated with a specific project.
*/
async deleteByProjectId(projectId: string): Promise<void> {
await this.collection.deleteMany({ projectId });
}
}

View file

@ -109,4 +109,8 @@ export class MongoDBConversationsRepository implements IConversationsRepository
nextCursor: hasNextPage ? results[limit - 1]._id.toString() : null,
};
}
async deleteByProjectId(projectId: string): Promise<void> {
await this.collection.deleteMany({ projectId });
}
}

View file

@ -267,4 +267,8 @@ export class MongoDBJobsRepository implements IJobsRepository {
nextCursor: hasNextPage ? results[_limit - 1]._id.toString() : null,
};
}
async deleteByProjectId(projectId: string): Promise<void> {
await this.collection.deleteMany({ projectId });
}
}

View file

@ -238,4 +238,8 @@ export class MongoDBRecurringJobRulesRepository implements IRecurringJobRulesRep
return this.convertDocToModel(result);
}
async deleteByProjectId(projectId: string): Promise<void> {
await this.collection.deleteMany({ projectId });
}
}

View file

@ -218,4 +218,8 @@ export class MongoDBScheduledJobRulesRepository implements IScheduledJobRulesRep
return result.deletedCount > 0;
}
async deleteByProjectId(projectId: string): Promise<void> {
await this.collection.deleteMany({ projectId });
}
}