Files
triggerdotdev--trigger.dev/apps/webapp/app/services/deleteProject.server.ts
Matt Aitken 9ae0ca64af Fix edge cases with deleting last org/project (#892)
* If you have an org with no projects, it displays in the project dropdown with a “New project” button

* When creating a new org disable the button whilst it’s doing the request

* If an org already had any deleted projects it couldn’t be deleted…

* When selecting the best project, factor in deleted ones

* Don’t show the cancel button when creating a new org if there are no non-deleted projects

* If a project has already been deleted just return
2024-02-09 11:05:28 +00:00

142 lines
3.4 KiB
TypeScript

import { PrismaClient } from "@trigger.dev/database";
import { prisma } from "~/db.server";
import { DeleteEndpointService } from "./endpoints/deleteEndpointService";
import { logger } from "./logger.server";
import { DisableScheduleSourceService } from "./schedules/disableScheduleSource.server";
type Options = ({ projectId: string } | { projectSlug: string }) & {
userId: string;
};
export class DeleteProjectService {
#prismaClient: PrismaClient;
constructor(prismaClient: PrismaClient = prisma) {
this.#prismaClient = prismaClient;
}
public async call(options: Options) {
const projectId = await this.#getProjectId(options);
const project = await this.#prismaClient.project.findFirst({
include: {
environments: {
include: {
endpoints: true,
},
},
jobs: {
where: { deletedAt: null },
include: {
aliases: {
where: {
name: "latest",
},
include: {
version: true,
},
take: 1,
},
},
},
organization: true,
},
where: {
id: projectId,
organization: { members: { some: { userId: options.userId } } },
},
});
if (!project) {
throw new Error("Project not found");
}
if (project.deletedAt) {
return;
}
//disable and delete all jobs
const service = new DisableScheduleSourceService();
for (const environment of project.environments) {
//disable the event dispatchers
await this.#prismaClient.eventDispatcher.updateMany({
where: {
environmentId: environment.id,
},
data: {
enabled: false,
},
});
const eventDispatchers = await this.#prismaClient.eventDispatcher.findMany({
where: {
environmentId: environment.id,
},
});
logger.info("Deleting jobs", { jobs: project.jobs });
for (const job of project.jobs) {
//disable all the job versions
await this.#prismaClient.jobVersion.updateMany({
where: {
jobId: job.id,
},
data: {
status: "DISABLED",
},
});
await this.#prismaClient.job.update({
where: {
id: job.id,
},
data: {
deletedAt: new Date(),
},
});
//disable scheduled sources
for (const eventDispatcher of eventDispatchers) {
await service.call({
key: job.id,
dispatcher: eventDispatcher,
});
}
}
}
//delete all endpoints
const deleteEndpointService = new DeleteEndpointService();
for (const environment of project.environments) {
for (const endpoint of environment.endpoints) {
await deleteEndpointService.call(endpoint.id, options.userId);
}
}
//mark the project as deleted
await this.#prismaClient.project.update({
where: {
id: project.id,
},
data: {
deletedAt: new Date(),
},
});
}
async #getProjectId(options: Options) {
if ("projectId" in options) {
return options.projectId;
}
const { id } = await this.#prismaClient.project.findFirstOrThrow({
select: {
id: true,
},
where: {
slug: options.projectSlug,
},
});
return id;
}
}