From c65e7315304575d0b33e779784352ff85f543ce0 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Wed, 20 Nov 2024 15:46:08 -0800 Subject: [PATCH 1/2] fix: Soft delete resources --- .../[agentId]/jobs/running/route.ts | 3 +- .../src/app/api/v1/jobs/[jobId]/route.ts | 4 +- .../deployment-to-resource/route.ts | 3 +- .../resource-to-resource/route.ts | 4 +- .../api/v1/resources/[resourceId]/route.ts | 33 +- .../identifier/[identifier]/route.ts | 20 +- .../api/src/router/deployment-variable.ts | 14 +- packages/api/src/router/deployment.ts | 5 +- packages/api/src/router/environment.ts | 15 +- packages/api/src/router/job.ts | 16 +- packages/api/src/router/release-deploy.ts | 13 +- packages/api/src/router/release.ts | 9 +- packages/api/src/router/resources.ts | 66 +- packages/api/src/router/target-provider.ts | 3 +- packages/api/src/router/workspace.ts | 2 +- packages/auth/src/utils/rbac.ts | 21 +- packages/db/drizzle/0041_smooth_tarantula.sql | 2 + packages/db/drizzle/meta/0041_snapshot.json | 4343 +++++++++++++++++ packages/db/drizzle/meta/_journal.json | 7 + packages/db/src/schema/job.ts | 4 +- packages/db/src/schema/resource-agent.ts | 10 - packages/db/src/schema/resource-group.ts | 8 +- packages/db/src/schema/resource-session.ts | 14 - packages/db/src/schema/resource.ts | 1 + .../__test__/job-variables-deployment.test.ts | 1 + .../job-dispatch/src/environment-creation.ts | 3 +- .../src/events/triggers/deployment-deleted.ts | 7 +- .../events/triggers/environment-deleted.ts | 10 +- .../src/events/triggers/resource-deleted.ts | 7 +- .../src/job-variables-deployment/utils.ts | 7 +- packages/job-dispatch/src/lock-checker.ts | 1 + .../job-dispatch/src/release-job-trigger.ts | 1 + packages/job-dispatch/src/resource.ts | 44 +- 33 files changed, 4583 insertions(+), 118 deletions(-) create mode 100644 packages/db/drizzle/0041_smooth_tarantula.sql create mode 100644 packages/db/drizzle/meta/0041_snapshot.json delete mode 100644 packages/db/src/schema/resource-agent.ts delete mode 100644 packages/db/src/schema/resource-session.ts diff --git a/apps/webservice/src/app/api/v1/job-agents/[agentId]/jobs/running/route.ts b/apps/webservice/src/app/api/v1/job-agents/[agentId]/jobs/running/route.ts index f726e878..1b2db59c 100644 --- a/apps/webservice/src/app/api/v1/job-agents/[agentId]/jobs/running/route.ts +++ b/apps/webservice/src/app/api/v1/job-agents/[agentId]/jobs/running/route.ts @@ -1,7 +1,7 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; -import { and, eq, notInArray } from "@ctrlplane/db"; +import { and, eq, isNull, notInArray } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import { deployment, @@ -40,6 +40,7 @@ export const GET = async ( "completed", "invalid_job_agent", ]), + isNull(resource.deletedAt), ), ) .then((rows) => diff --git a/apps/webservice/src/app/api/v1/jobs/[jobId]/route.ts b/apps/webservice/src/app/api/v1/jobs/[jobId]/route.ts index 57c88cd1..14b7d9ee 100644 --- a/apps/webservice/src/app/api/v1/jobs/[jobId]/route.ts +++ b/apps/webservice/src/app/api/v1/jobs/[jobId]/route.ts @@ -1,7 +1,7 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; -import { and, eq, takeFirstOrNull } from "@ctrlplane/db"; +import { and, eq, isNull, takeFirstOrNull } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import { deployment, @@ -83,7 +83,7 @@ export const GET = request() .leftJoin(resource, eq(resource.id, releaseJobTrigger.resourceId)) .leftJoin(release, eq(release.id, releaseJobTrigger.releaseId)) .leftJoin(deployment, eq(deployment.id, release.deploymentId)) - .where(eq(job.id, params.jobId)) + .where(and(eq(job.id, params.jobId), isNull(resource.deletedAt))) .then(takeFirstOrNull); if (row == null) diff --git a/apps/webservice/src/app/api/v1/relationship/deployment-to-resource/route.ts b/apps/webservice/src/app/api/v1/relationship/deployment-to-resource/route.ts index 5313543c..637d5265 100644 --- a/apps/webservice/src/app/api/v1/relationship/deployment-to-resource/route.ts +++ b/apps/webservice/src/app/api/v1/relationship/deployment-to-resource/route.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { and, eq, takeFirstOrNull } from "@ctrlplane/db"; +import { and, eq, isNull, takeFirstOrNull } from "@ctrlplane/db"; import * as SCHEMA from "@ctrlplane/db/schema"; import { authn } from "../../auth"; @@ -27,6 +27,7 @@ export const POST = request() and( eq(SCHEMA.resource.identifier, body.resourceIdentifier), eq(SCHEMA.resource.workspaceId, body.workspaceId), + isNull(SCHEMA.resource.deletedAt), ), ) .then(takeFirstOrNull); diff --git a/apps/webservice/src/app/api/v1/relationship/resource-to-resource/route.ts b/apps/webservice/src/app/api/v1/relationship/resource-to-resource/route.ts index 37ad36f3..96f5b355 100644 --- a/apps/webservice/src/app/api/v1/relationship/resource-to-resource/route.ts +++ b/apps/webservice/src/app/api/v1/relationship/resource-to-resource/route.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { and, eq, takeFirstOrNull } from "@ctrlplane/db"; +import { and, eq, isNull, takeFirstOrNull } from "@ctrlplane/db"; import * as SCHEMA from "@ctrlplane/db/schema"; import { authn } from "../../auth"; @@ -28,6 +28,7 @@ export const POST = request() and( eq(SCHEMA.resource.identifier, body.fromIdentifier), eq(SCHEMA.resource.workspaceId, body.workspaceId), + isNull(SCHEMA.resource.deletedAt), ), ) .then(takeFirstOrNull); @@ -44,6 +45,7 @@ export const POST = request() and( eq(SCHEMA.resource.identifier, body.toIdentifier), eq(SCHEMA.resource.workspaceId, body.workspaceId), + isNull(SCHEMA.resource.deletedAt), ), ) .then(takeFirstOrNull); diff --git a/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts b/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts index 42a83e1a..50792298 100644 --- a/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts +++ b/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts @@ -2,9 +2,9 @@ import { NextResponse } from "next/server"; import _ from "lodash"; import { z } from "zod"; -import { eq } from "@ctrlplane/db"; +import { and, eq, isNull } from "@ctrlplane/db"; import * as schema from "@ctrlplane/db/schema"; -import { upsertResources } from "@ctrlplane/job-dispatch"; +import { deleteResources, upsertResources } from "@ctrlplane/job-dispatch"; import { variablesAES256 } from "@ctrlplane/secrets"; import { Permission } from "@ctrlplane/validators/auth"; @@ -22,13 +22,11 @@ export const GET = request() }), ) .handle(async ({ db }, { params }: { params: { resourceId: string } }) => { + const isResource = eq(schema.resource.id, params.resourceId); + const isNotDeleted = isNull(schema.resource.deletedAt); const data = await db.query.resource.findFirst({ - where: eq(schema.resource.id, params.resourceId), - with: { - metadata: true, - variables: true, - provider: true, - }, + where: and(isResource, isNotDeleted), + with: { metadata: true, variables: true, provider: true }, }); if (data == null) @@ -85,9 +83,10 @@ export const PATCH = request() { body: z.infer }, { params: { resourceId: string } } >(async ({ db, body }, { params }) => { - const resource = await db.query.resource.findFirst({ - where: eq(schema.resource.id, params.resourceId), - }); + const isResource = eq(schema.resource.id, params.resourceId); + const isNotDeleted = isNull(schema.resource.deletedAt); + const where = and(isResource, isNotDeleted); + const resource = await db.query.resource.findFirst({ where }); if (resource == null) return NextResponse.json( @@ -110,9 +109,10 @@ export const DELETE = request() ), ) .handle(async ({ db }, { params }: { params: { resourceId: string } }) => { - const resource = await db.query.resource.findFirst({ - where: eq(schema.resource.id, params.resourceId), - }); + const isResource = eq(schema.resource.id, params.resourceId); + const isNotDeleted = isNull(schema.resource.deletedAt); + const where = and(isResource, isNotDeleted); + const resource = await db.query.resource.findFirst({ where }); if (resource == null) return NextResponse.json( @@ -120,9 +120,6 @@ export const DELETE = request() { status: 404 }, ); - await db - .delete(schema.resource) - .where(eq(schema.resource.id, params.resourceId)); - + await deleteResources(db, [resource]); return NextResponse.json({ success: true }); }); diff --git a/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts b/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts index 37b75b92..8fda6ff8 100644 --- a/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts +++ b/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server"; -import { and, eq } from "@ctrlplane/db"; +import { and, eq, isNull } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import * as schema from "@ctrlplane/db/schema"; import { Permission } from "@ctrlplane/validators/auth"; @@ -14,17 +14,18 @@ export const GET = request() authz(async ({ can, extra }) => { const { workspaceId, identifier } = extra; - const target = await db.query.resource.findFirst({ + const resource = await db.query.resource.findFirst({ where: and( eq(schema.resource.workspaceId, workspaceId), eq(schema.resource.identifier, identifier), + isNull(schema.resource.deletedAt), ), }); - if (target == null) return false; + if (resource == null) return false; return can .perform(Permission.ResourceGet) - .on({ type: "resource", id: target.id }); + .on({ type: "resource", id: resource.id }); }), ) .handle( @@ -33,6 +34,7 @@ export const GET = request() where: and( eq(schema.resource.workspaceId, params.workspaceId), eq(schema.resource.identifier, params.identifier), + isNull(schema.resource.deletedAt), ), with: { metadata: true, @@ -66,6 +68,7 @@ export const DELETE = request() where: and( eq(schema.resource.workspaceId, workspaceId), eq(schema.resource.identifier, identifier), + isNull(schema.resource.deletedAt), ), }); @@ -77,21 +80,24 @@ export const DELETE = request() ) .handle( async (_, { params }) => { - const target = await db.query.resource.findFirst({ + const resource = await db.query.resource.findFirst({ where: and( eq(schema.resource.workspaceId, params.workspaceId), eq(schema.resource.identifier, params.identifier), + isNull(schema.resource.deletedAt), ), }); - if (target == null) { + if (resource == null) { return NextResponse.json( { error: `Target not found for identifier: ${params.identifier}` }, { status: 404 }, ); } - await db.delete(schema.resource).where(eq(schema.resource.id, target.id)); + await db + .delete(schema.resource) + .where(eq(schema.resource.id, resource.id)); return NextResponse.json({ success: true }); }, diff --git a/packages/api/src/router/deployment-variable.ts b/packages/api/src/router/deployment-variable.ts index e8b62844..feb18002 100644 --- a/packages/api/src/router/deployment-variable.ts +++ b/packages/api/src/router/deployment-variable.ts @@ -9,6 +9,7 @@ import { asc, eq, isNotNull, + isNull, ne, sql, takeFirst, @@ -214,11 +215,17 @@ const valueRouter = createTRPCRouter({ }; const oldTargets = await ctx.db.query.resource.findMany({ - where: resourceMatchesMetadata(ctx.db, oldTargetFilter), + where: and( + resourceMatchesMetadata(ctx.db, oldTargetFilter), + isNull(resource.deletedAt), + ), }); const newTargets = await ctx.db.query.resource.findMany({ - where: resourceMatchesMetadata(ctx.db, newTargetFilter), + where: and( + resourceMatchesMetadata(ctx.db, newTargetFilter), + isNull(resource.deletedAt), + ), }); const oldTargetIds = new Set(oldTargets.map((t) => t.id)); @@ -299,7 +306,6 @@ export const deploymentVariableRouter = createTRPCRouter({ const deploymentVariables = await ctx.db .select() .from(resource) - .where(eq(resource.id, input)) .innerJoin(system, eq(resource.workspaceId, system.workspaceId)) .innerJoin(deployment, eq(deployment.systemId, system.id)) .innerJoin( @@ -310,6 +316,7 @@ export const deploymentVariableRouter = createTRPCRouter({ deploymentVariableValue, eq(deploymentVariableValue.variableId, deploymentVariable.id), ) + .where(and(eq(resource.id, input), isNull(resource.deletedAt))) .then((rows) => _.chain(rows) .groupBy((r) => r.deployment_variable.id) @@ -331,6 +338,7 @@ export const deploymentVariableRouter = createTRPCRouter({ .where( and( eq(resource.id, input), + isNull(resource.deletedAt), resourceMatchesMetadata(ctx.db, targetFilter), ), ) diff --git a/packages/api/src/router/deployment.ts b/packages/api/src/router/deployment.ts index d01b1942..dc7e1829 100644 --- a/packages/api/src/router/deployment.ts +++ b/packages/api/src/router/deployment.ts @@ -9,6 +9,7 @@ import { eq, inArray, isNotNull, + isNull, sql, takeFirst, takeFirstOrNull, @@ -417,6 +418,7 @@ export const deploymentRouter = createTRPCRouter({ and( eq(release.deploymentId, input), eq(latestJobsPerResource.rank, 1), + isNull(resource.deletedAt), ), ) .then((r) => @@ -620,7 +622,7 @@ export const deploymentRouter = createTRPCRouter({ const tg = await ctx.db .select() .from(resource) - .where(eq(resource.id, input)) + .where(and(eq(resource.id, input), isNull(resource.deletedAt))) .then(takeFirst); const envs = await ctx.db @@ -659,6 +661,7 @@ export const deploymentRouter = createTRPCRouter({ .where( and( eq(resource.id, input), + isNull(resource.deletedAt), inArray(job.status, [ JobStatus.Completed, JobStatus.Pending, diff --git a/packages/api/src/router/environment.ts b/packages/api/src/router/environment.ts index b27e5ea7..b7d95f7b 100644 --- a/packages/api/src/router/environment.ts +++ b/packages/api/src/router/environment.ts @@ -10,6 +10,7 @@ import { eq, inArray, isNotNull, + isNull, ne, not, takeFirst, @@ -170,9 +171,12 @@ export const environmentRouter = createTRPCRouter({ .select() .from(resource) .where( - resourceMatchesMetadata( - ctx.db, - e.environment.resourceFilter, + and( + isNull(resource.deletedAt), + resourceMatchesMetadata( + ctx.db, + e.environment.resourceFilter, + ), ), ) : [], @@ -269,6 +273,7 @@ export const environmentRouter = createTRPCRouter({ .where( and( eq(resource.workspaceId, oldEnv.system.workspaceId), + isNull(resource.deletedAt), newQuery, oldQuery && not(oldQuery), ), @@ -277,6 +282,7 @@ export const environmentRouter = createTRPCRouter({ const removedResources = await ctx.db.query.resource.findMany({ where: and( eq(resource.workspaceId, oldEnv.system.workspaceId), + isNull(resource.deletedAt), oldQuery, newQuery && not(newQuery), ), @@ -299,10 +305,11 @@ export const environmentRouter = createTRPCRouter({ otherEnvFilters.length > 0 ? resourceMatchesMetadata(ctx.db, sysFilter) : undefined; + const isNotDeleted = isNull(resource.deletedAt); const removedFromSystemResources = await ctx.db.query.resource.findMany({ - where: and(isRemovedFromEnv, isRemovedFromSystem), + where: and(isRemovedFromEnv, isRemovedFromSystem, isNotDeleted), }); const events = removedFromSystemResources.flatMap((resource) => diff --git a/packages/api/src/router/job.ts b/packages/api/src/router/job.ts index 779ca7be..2bed3917 100644 --- a/packages/api/src/router/job.ts +++ b/packages/api/src/router/job.ts @@ -25,6 +25,7 @@ import { desc, eq, inArray, + isNull, notInArray, sql, takeFirst, @@ -162,6 +163,7 @@ const releaseJobTriggerRouter = createTRPCRouter({ .where( and( eq(system.workspaceId, input.workspaceId), + isNull(resource.deletedAt), jobMatchesCondition(ctx.db, input.filter), ), ) @@ -196,6 +198,7 @@ const releaseJobTriggerRouter = createTRPCRouter({ .where( and( eq(system.workspaceId, input.workspaceId), + isNull(resource.deletedAt), jobMatchesCondition(ctx.db, input.filter), ), ) @@ -284,6 +287,7 @@ const releaseJobTriggerRouter = createTRPCRouter({ and( eq(deployment.id, input.deploymentId), eq(environment.id, input.environmentId), + isNull(resource.deletedAt), ), ) .then((data) => @@ -327,6 +331,7 @@ const releaseJobTriggerRouter = createTRPCRouter({ .where( and( eq(release.id, input.releaseId), + isNull(resource.deletedAt), jobMatchesCondition(ctx.db, input.filter), ), ) @@ -371,7 +376,7 @@ const releaseJobTriggerRouter = createTRPCRouter({ deploymentName, eq(deploymentName.deploymentId, releaseDependency.deploymentId), ) - .where(eq(job.id, input)) + .where(and(eq(job.id, input), isNull(resource.deletedAt))) .then(processReleaseJobTriggerWithAdditionalDataRows) .then(takeFirst); @@ -435,7 +440,7 @@ const releaseJobTriggerRouter = createTRPCRouter({ const resources = await ctx.db .select() .from(resource) - .where(inArray(resource.id, allIds)); + .where(and(inArray(resource.id, allIds), isNull(resource.deletedAt))); const releaseDependenciesWithResourcePromises = releaseDependencies.map( async (rd) => { @@ -467,7 +472,10 @@ const releaseJobTriggerRouter = createTRPCRouter({ and( releaseMatchesCondition(ctx.db, rd.releaseFilter), eq(deployment.id, rd.deploymentId), - inArray(latestJobSubquery.resourceId, allIds), + inArray( + latestJobSubquery.resourceId, + resources.map((r) => r.id), + ), eq(latestJobSubquery.rank, 1), eq(latestJobSubquery.status, JobStatus.Completed), ), @@ -622,7 +630,7 @@ export const jobRouter = createTRPCRouter({ .input(z.string()) .query(({ ctx, input }) => releaseJobTriggerQuery(ctx.db) - .where(eq(resource.id, input)) + .where(and(eq(resource.id, input), isNull(resource.deletedAt))) .limit(1_000) .orderBy(desc(job.createdAt), desc(releaseJobTrigger.createdAt)) .then((data) => diff --git a/packages/api/src/router/release-deploy.ts b/packages/api/src/router/release-deploy.ts index 5109e340..08b089db 100644 --- a/packages/api/src/router/release-deploy.ts +++ b/packages/api/src/router/release-deploy.ts @@ -3,7 +3,14 @@ import type { ReleaseJobTrigger } from "@ctrlplane/db/schema"; import _ from "lodash"; import { z } from "zod"; -import { and, eq, inArray, notInArray, takeFirstOrNull } from "@ctrlplane/db"; +import { + and, + eq, + inArray, + isNull, + notInArray, + takeFirstOrNull, +} from "@ctrlplane/db"; import { environment, job, @@ -123,7 +130,9 @@ export const releaseDeployRouter = createTRPCRouter({ const t = await ctx.db .select() .from(resource) - .where(eq(resource.id, input.resourceId)) + .where( + and(eq(resource.id, input.resourceId), isNull(resource.deletedAt)), + ) .then(takeFirstOrNull); if (!t) throw new Error("Resource not found"); diff --git a/packages/api/src/router/release.ts b/packages/api/src/router/release.ts index 36245ad8..6a4626df 100644 --- a/packages/api/src/router/release.ts +++ b/packages/api/src/router/release.ts @@ -8,6 +8,7 @@ import { desc, eq, inArray, + isNull, takeFirst, takeFirstOrNull, } from "@ctrlplane/db"; @@ -104,7 +105,13 @@ export const releaseRouter = createTRPCRouter({ .select() .from(releaseJobTrigger) .innerJoin(job, eq(releaseJobTrigger.jobId, job.id)) - .innerJoin(resource, eq(releaseJobTrigger.resourceId, resource.id)) + .innerJoin( + resource, + and( + eq(releaseJobTrigger.resourceId, resource.id), + isNull(resource.deletedAt), + ), + ) .where( inArray( releaseJobTrigger.releaseId, diff --git a/packages/api/src/router/resources.ts b/packages/api/src/router/resources.ts index 9954d16f..54f9f37f 100644 --- a/packages/api/src/router/resources.ts +++ b/packages/api/src/router/resources.ts @@ -11,6 +11,7 @@ import { eq, inArray, isNotNull, + isNull, not, sql, takeFirst, @@ -35,10 +36,17 @@ import { createTRPCRouter, protectedProcedure } from "../trpc"; import { resourceMetadataGroupRouter } from "./target-metadata-group"; import { resourceProviderRouter } from "./target-provider"; +const isNotDeleted = isNull(schema.resource.deletedAt); + const resourceRelations = createTRPCRouter({ hierarchy: protectedProcedure .input(z.string().uuid()) .query(async ({ ctx, input }) => { + const isResource = eq(schema.resource.id, input); + const where = and(isResource, isNotDeleted); + const r = await ctx.db.query.resource.findFirst({ where }); + if (r == null) return null; + const results = await ctx.db.execute( sql` WITH RECURSIVE reachable_relationships(id, visited, tr_id, source_id, target_id, type) AS ( @@ -96,7 +104,7 @@ const resourceRelations = createTRPCRouter({ const resources = await ctx.db .select() .from(schema.resource) - .where(inArray(schema.resource.id, allIds)); + .where(and(inArray(schema.resource.id, allIds), isNotDeleted)); return { relationships, resources }; }), @@ -186,14 +194,16 @@ const resourceViews = createTRPCRouter({ const total = await ctx.db .select({ count: count() }) .from(schema.resource) - .where(schema.resourceMatchesMetadata(ctx.db, view.filter)) + .where( + and( + schema.resourceMatchesMetadata(ctx.db, view.filter), + isNotDeleted, + ), + ) .then(takeFirst) .then((t) => t.count); - return { - ...view, - total, - }; + return { ...view, total }; }), ); }), @@ -322,7 +332,7 @@ export const resourceRouter = createTRPCRouter({ .query(({ ctx, input }) => ctx.db.query.resource .findFirst({ - where: eq(schema.resource.id, input), + where: and(eq(schema.resource.id, input), isNotDeleted), with: { metadata: true, variables: true, provider: true }, }) .then((t) => { @@ -338,16 +348,13 @@ export const resourceRouter = createTRPCRouter({ .query(async ({ ctx, input }) => { const hasFilter = isNotNull(schema.environment.resourceFilter); const resource = await ctx.db.query.resource.findFirst({ - where: eq(schema.resource.id, input), + where: and(eq(schema.resource.id, input), isNotDeleted), with: { provider: { with: { google: true } }, workspace: { with: { systems: { - with: { - environments: { where: hasFilter }, - deployments: true, - }, + with: { environments: { where: hasFilter }, deployments: true }, }, }, }, @@ -363,6 +370,7 @@ export const resourceRouter = createTRPCRouter({ where: and( eq(schema.resource.id, resource.id), schema.resourceMatchesMetadata(ctx.db, e.resourceFilter), + isNotDeleted, ), }); @@ -428,7 +436,11 @@ export const resourceRouter = createTRPCRouter({ ctx.db, input.filter, ); - const checks = [workspaceIdCheck, resourceConditions].filter(isPresent); + const checks = [ + workspaceIdCheck, + resourceConditions, + isNotDeleted, + ].filter(isPresent); const properties = { kind: schema.resource.kind, @@ -524,9 +536,10 @@ export const resourceRouter = createTRPCRouter({ const updatedResource = await tx .update(schema.resource) .set(data) - .where(eq(schema.resource.id, id)) + .where(and(eq(schema.resource.id, id), isNotDeleted)) .returning() - .then(takeFirst); + .then(takeFirstOrNull); + if (updatedResource == null) return null; const metadataEntries = Object.entries(data.metadata).map( ([key, value]) => ({ @@ -579,7 +592,9 @@ export const resourceRouter = createTRPCRouter({ .input(z.array(z.string().uuid())) .mutation(async ({ ctx, input }) => ctx.db.query.resource - .findMany({ where: inArray(schema.resource.id, input) }) + .findMany({ + where: and(inArray(schema.resource.id, input), isNotDeleted), + }) .then((resources) => deleteResources(ctx.db, resources)), ), @@ -599,7 +614,7 @@ export const resourceRouter = createTRPCRouter({ schema.resourceMetadata, eq(schema.resourceMetadata.resourceId, schema.resource.id), ) - .where(eq(schema.resource.workspaceId, input)) + .where(and(eq(schema.resource.workspaceId, input), isNotDeleted)) .then((r) => r.map((row) => row.key)), ), @@ -615,7 +630,7 @@ export const resourceRouter = createTRPCRouter({ ctx.db .update(schema.resource) .set({ lockedAt: new Date() }) - .where(eq(schema.resource.id, input)) + .where(and(eq(schema.resource.id, input), isNotDeleted)) .returning() .then(takeFirst), ), @@ -632,7 +647,7 @@ export const resourceRouter = createTRPCRouter({ ctx.db .update(schema.resource) .set({ lockedAt: null }) - .where(eq(schema.resource.id, input)) + .where(and(eq(schema.resource.id, input), isNotDeleted)) .returning() .then(takeFirst), ), @@ -645,8 +660,13 @@ export const resourceRouter = createTRPCRouter({ .perform(Permission.ResourceUpdate) .on({ type: "resource", id: input }), }) - .mutation(({ ctx, input }) => - createReleaseJobTriggers(ctx.db, "redeploy") + .mutation(async ({ ctx, input }) => { + const resource = await ctx.db.query.resource.findFirst({ + where: and(eq(schema.resource.id, input), isNotDeleted), + }); + if (resource == null) return null; + + return createReleaseJobTriggers(ctx.db, "redeploy") .causedById(ctx.session.user.id) .resources([input]) .filter(isPassingReleaseStringCheckPolicy) @@ -659,6 +679,6 @@ export const resourceRouter = createTRPCRouter({ .filter(isPassingAllPoliciesExceptNewerThanLastActive) .then(cancelOldReleaseJobTriggersOnJobDispatch) .dispatch(), - ), - ), + ); + }), }); diff --git a/packages/api/src/router/target-provider.ts b/packages/api/src/router/target-provider.ts index 8c7e3998..a7ca8cbd 100644 --- a/packages/api/src/router/target-provider.ts +++ b/packages/api/src/router/target-provider.ts @@ -224,7 +224,8 @@ export const resourceProviderRouter = createTRPCRouter({ ctx.db.transaction(async (tx) => { if (input.deleteResources) await tx - .delete(resource) + .update(resource) + .set({ deletedAt: new Date() }) .where(eq(resource.providerId, input.providerId)); const deletedProvider = await tx diff --git a/packages/api/src/router/workspace.ts b/packages/api/src/router/workspace.ts index 68a5c832..4e19c4a0 100644 --- a/packages/api/src/router/workspace.ts +++ b/packages/api/src/router/workspace.ts @@ -230,7 +230,7 @@ export const workspaceRouter = createTRPCRouter({ count: sql`count(*)`.mapWith(Number), }) .from(resource) - .where(eq(resource.workspaceId, input)) + .where(and(eq(resource.workspaceId, input), isNull(resource.deletedAt))) .groupBy(resource.version, resource.kind) .orderBy(asc(resource.kind)); diff --git a/packages/auth/src/utils/rbac.ts b/packages/auth/src/utils/rbac.ts index 4f22b662..55c7a9e5 100644 --- a/packages/auth/src/utils/rbac.ts +++ b/packages/auth/src/utils/rbac.ts @@ -1,6 +1,13 @@ import type { EntityType, ScopeType } from "@ctrlplane/db/schema"; -import { and, eq, inArray, takeFirst, takeFirstOrNull } from "@ctrlplane/db"; +import { + and, + eq, + inArray, + isNull, + takeFirst, + takeFirstOrNull, +} from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import { deployment, @@ -185,13 +192,15 @@ const getTargetMetadataGroupScopes = async (id: string) => { ]; }; -const getTargetScopes = async (id: string) => { +const getResourceScopes = async (id: string) => { const result = await db .select() .from(workspace) .innerJoin(resource, eq(resource.workspaceId, workspace.id)) - .where(eq(resource.id, id)) - .then(takeFirst); + .where(and(eq(resource.id, id), isNull(resource.deletedAt))) + .then(takeFirstOrNull); + + if (result == null) return []; return [ { type: "resource" as const, id: result.resource.id }, @@ -347,7 +356,7 @@ const getJobScopes = async (id: string) => { .innerJoin(deployment, eq(release.deploymentId, deployment.id)) .innerJoin(system, eq(deployment.systemId, system.id)) .innerJoin(workspace, eq(system.workspaceId, workspace.id)) - .where(eq(job.id, id)) + .where(and(eq(job.id, id), isNull(resource.deletedAt))) .then(takeFirstOrNull); if (result == null) return []; @@ -369,7 +378,7 @@ export const scopeHandlers: Record< ScopeType, (id: string) => Promise> > = { - resource: getTargetScopes, + resource: getResourceScopes, resourceView: getTargetViewScopes, resourceProvider: getTargetProviderScopes, deployment: getDeploymentScopes, diff --git a/packages/db/drizzle/0041_smooth_tarantula.sql b/packages/db/drizzle/0041_smooth_tarantula.sql new file mode 100644 index 00000000..1ee6651d --- /dev/null +++ b/packages/db/drizzle/0041_smooth_tarantula.sql @@ -0,0 +1,2 @@ +DROP TABLE "resource_session";--> statement-breakpoint +ALTER TABLE "resource" ADD COLUMN "deleted_at" timestamp with time zone; \ No newline at end of file diff --git a/packages/db/drizzle/meta/0041_snapshot.json b/packages/db/drizzle/meta/0041_snapshot.json new file mode 100644 index 00000000..054d3d29 --- /dev/null +++ b/packages/db/drizzle/meta/0041_snapshot.json @@ -0,0 +1,4343 @@ +{ + "id": "b49d6d8e-a24d-443d-973b-162c71b7d5e5", + "prevId": "b7e6477c-5944-408f-aaaa-380b918c9ea1", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": ["provider", "providerAccountId"] + } + }, + "uniqueConstraints": {} + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "active_workspace_id": { + "name": "active_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "user_active_workspace_id_workspace_id_fk": { + "name": "user_active_workspace_id_workspace_id_fk", + "tableFrom": "user", + "tableTo": "workspace", + "columnsFrom": ["active_workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.user_api_key": { + "name": "user_api_key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "key_preview": { + "name": "key_preview", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_prefix": { + "name": "key_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_api_key_key_prefix_key_hash_index": { + "name": "user_api_key_key_prefix_key_hash_index", + "columns": [ + { + "expression": "key_prefix", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_api_key_user_id_user_id_fk": { + "name": "user_api_key_user_id_user_id_fk", + "tableFrom": "user_api_key", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.dashboard": { + "name": "dashboard", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "dashboard_workspace_id_workspace_id_fk": { + "name": "dashboard_workspace_id_workspace_id_fk", + "tableFrom": "dashboard", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.dashboard_widget": { + "name": "dashboard_widget", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "dashboard_id": { + "name": "dashboard_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "widget": { + "name": "widget", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "x": { + "name": "x", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "y": { + "name": "y", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "w": { + "name": "w", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "h": { + "name": "h", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "dashboard_widget_dashboard_id_dashboard_id_fk": { + "name": "dashboard_widget_dashboard_id_dashboard_id_fk", + "tableFrom": "dashboard_widget", + "tableTo": "dashboard", + "columnsFrom": ["dashboard_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment_variable": { + "name": "deployment_variable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "default_value_id": { + "name": "default_value_id", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "NULL" + }, + "schema": { + "name": "schema", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "deployment_variable_deployment_id_key_index": { + "name": "deployment_variable_deployment_id_key_index", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_variable_deployment_id_deployment_id_fk": { + "name": "deployment_variable_deployment_id_deployment_id_fk", + "tableFrom": "deployment_variable", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployment_variable_default_value_id_deployment_variable_value_id_fk": { + "name": "deployment_variable_default_value_id_deployment_variable_value_id_fk", + "tableFrom": "deployment_variable", + "tableTo": "deployment_variable_value", + "columnsFrom": ["default_value_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment_variable_set": { + "name": "deployment_variable_set", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "variable_set_id": { + "name": "variable_set_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "deployment_variable_set_deployment_id_variable_set_id_index": { + "name": "deployment_variable_set_deployment_id_variable_set_id_index", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "variable_set_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_variable_set_deployment_id_deployment_id_fk": { + "name": "deployment_variable_set_deployment_id_deployment_id_fk", + "tableFrom": "deployment_variable_set", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployment_variable_set_variable_set_id_variable_set_id_fk": { + "name": "deployment_variable_set_variable_set_id_variable_set_id_fk", + "tableFrom": "deployment_variable_set", + "tableTo": "variable_set", + "columnsFrom": ["variable_set_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment_variable_value": { + "name": "deployment_variable_value", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variable_id": { + "name": "variable_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "resource_filter": { + "name": "resource_filter", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "NULL" + } + }, + "indexes": { + "deployment_variable_value_variable_id_value_index": { + "name": "deployment_variable_value_variable_id_value_index", + "columns": [ + { + "expression": "variable_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "value", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_variable_value_variable_id_deployment_variable_id_fk": { + "name": "deployment_variable_value_variable_id_deployment_variable_id_fk", + "tableFrom": "deployment_variable_value", + "tableTo": "deployment_variable", + "columnsFrom": ["variable_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "system_id": { + "name": "system_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "job_agent_id": { + "name": "job_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "job_agent_config": { + "name": "job_agent_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + } + }, + "indexes": { + "deployment_system_id_slug_index": { + "name": "deployment_system_id_slug_index", + "columns": [ + { + "expression": "system_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_system_id_system_id_fk": { + "name": "deployment_system_id_system_id_fk", + "tableFrom": "deployment", + "tableTo": "system", + "columnsFrom": ["system_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployment_job_agent_id_job_agent_id_fk": { + "name": "deployment_job_agent_id_job_agent_id_fk", + "tableFrom": "deployment", + "tableTo": "job_agent", + "columnsFrom": ["job_agent_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment_meta_dependency": { + "name": "deployment_meta_dependency", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "depends_on_id": { + "name": "depends_on_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "deployment_meta_dependency_depends_on_id_deployment_id_index": { + "name": "deployment_meta_dependency_depends_on_id_deployment_id_index", + "columns": [ + { + "expression": "depends_on_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_meta_dependency_deployment_id_deployment_id_fk": { + "name": "deployment_meta_dependency_deployment_id_deployment_id_fk", + "tableFrom": "deployment_meta_dependency", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployment_meta_dependency_depends_on_id_deployment_id_fk": { + "name": "deployment_meta_dependency_depends_on_id_deployment_id_fk", + "tableFrom": "deployment_meta_dependency", + "tableTo": "deployment", + "columnsFrom": ["depends_on_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "system_id": { + "name": "system_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "resource_filter": { + "name": "resource_filter", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "NULL" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "NULL" + } + }, + "indexes": { + "environment_system_id_name_index": { + "name": "environment_system_id_name_index", + "columns": [ + { + "expression": "system_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_system_id_system_id_fk": { + "name": "environment_system_id_system_id_fk", + "tableFrom": "environment", + "tableTo": "system", + "columnsFrom": ["system_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_policy_id_environment_policy_id_fk": { + "name": "environment_policy_id_environment_policy_id_fk", + "tableFrom": "environment", + "tableTo": "environment_policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment_metadata": { + "name": "environment_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "environment_metadata_key_environment_id_index": { + "name": "environment_metadata_key_environment_id_index", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_metadata_environment_id_environment_id_fk": { + "name": "environment_metadata_environment_id_environment_id_fk", + "tableFrom": "environment_metadata", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment_policy": { + "name": "environment_policy", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_id": { + "name": "system_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_required": { + "name": "approval_required", + "type": "environment_policy_approval_requirement", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "success_status": { + "name": "success_status", + "type": "environment_policy_deployment_success_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "minimum_success": { + "name": "minimum_success", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "concurrency_type": { + "name": "concurrency_type", + "type": "concurrency_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "concurrency_limit": { + "name": "concurrency_limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "rollout_duration": { + "name": "rollout_duration", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "release_sequencing": { + "name": "release_sequencing", + "type": "release_sequencing_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cancel'" + } + }, + "indexes": {}, + "foreignKeys": { + "environment_policy_system_id_system_id_fk": { + "name": "environment_policy_system_id_system_id_fk", + "tableFrom": "environment_policy", + "tableTo": "system", + "columnsFrom": ["system_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment_policy_approval": { + "name": "environment_policy_approval", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "release_id": { + "name": "release_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "approval_status_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "environment_policy_approval_policy_id_release_id_index": { + "name": "environment_policy_approval_policy_id_release_id_index", + "columns": [ + { + "expression": "policy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "release_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_policy_approval_policy_id_environment_policy_id_fk": { + "name": "environment_policy_approval_policy_id_environment_policy_id_fk", + "tableFrom": "environment_policy_approval", + "tableTo": "environment_policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_policy_approval_release_id_release_id_fk": { + "name": "environment_policy_approval_release_id_release_id_fk", + "tableFrom": "environment_policy_approval", + "tableTo": "release", + "columnsFrom": ["release_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_policy_approval_user_id_user_id_fk": { + "name": "environment_policy_approval_user_id_user_id_fk", + "tableFrom": "environment_policy_approval", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment_policy_deployment": { + "name": "environment_policy_deployment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "environment_policy_deployment_policy_id_environment_id_index": { + "name": "environment_policy_deployment_policy_id_environment_id_index", + "columns": [ + { + "expression": "policy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_policy_deployment_policy_id_environment_policy_id_fk": { + "name": "environment_policy_deployment_policy_id_environment_policy_id_fk", + "tableFrom": "environment_policy_deployment", + "tableTo": "environment_policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_policy_deployment_environment_id_environment_id_fk": { + "name": "environment_policy_deployment_environment_id_environment_id_fk", + "tableFrom": "environment_policy_deployment", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment_policy_release_channel": { + "name": "environment_policy_release_channel", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "channel_id": { + "name": "channel_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "environment_policy_release_channel_policy_id_channel_id_index": { + "name": "environment_policy_release_channel_policy_id_channel_id_index", + "columns": [ + { + "expression": "policy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_policy_release_channel_policy_id_deployment_id_index": { + "name": "environment_policy_release_channel_policy_id_deployment_id_index", + "columns": [ + { + "expression": "policy_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_policy_release_channel_policy_id_environment_policy_id_fk": { + "name": "environment_policy_release_channel_policy_id_environment_policy_id_fk", + "tableFrom": "environment_policy_release_channel", + "tableTo": "environment_policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_policy_release_channel_channel_id_release_channel_id_fk": { + "name": "environment_policy_release_channel_channel_id_release_channel_id_fk", + "tableFrom": "environment_policy_release_channel", + "tableTo": "release_channel", + "columnsFrom": ["channel_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_policy_release_channel_deployment_id_deployment_id_fk": { + "name": "environment_policy_release_channel_deployment_id_deployment_id_fk", + "tableFrom": "environment_policy_release_channel", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment_policy_release_window": { + "name": "environment_policy_release_window", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "timestamp (0) with time zone", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp (0) with time zone", + "primaryKey": false, + "notNull": true + }, + "recurrence": { + "name": "recurrence", + "type": "recurrence_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "environment_policy_release_window_policy_id_environment_policy_id_fk": { + "name": "environment_policy_release_window_policy_id_environment_policy_id_fk", + "tableFrom": "environment_policy_release_window", + "tableTo": "environment_policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.environment_release_channel": { + "name": "environment_release_channel", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "channel_id": { + "name": "channel_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "environment_release_channel_environment_id_channel_id_index": { + "name": "environment_release_channel_environment_id_channel_id_index", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "environment_release_channel_environment_id_deployment_id_index": { + "name": "environment_release_channel_environment_id_deployment_id_index", + "columns": [ + { + "expression": "environment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "environment_release_channel_environment_id_environment_id_fk": { + "name": "environment_release_channel_environment_id_environment_id_fk", + "tableFrom": "environment_release_channel", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_release_channel_channel_id_release_channel_id_fk": { + "name": "environment_release_channel_channel_id_release_channel_id_fk", + "tableFrom": "environment_release_channel", + "tableTo": "release_channel", + "columnsFrom": ["channel_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "environment_release_channel_deployment_id_deployment_id_fk": { + "name": "environment_release_channel_deployment_id_deployment_id_fk", + "tableFrom": "environment_release_channel", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.event": { + "name": "event", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.hook": { + "name": "hook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.runhook": { + "name": "runhook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "hook_id": { + "name": "hook_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "runbook_id": { + "name": "runbook_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "runhook_hook_id_runbook_id_index": { + "name": "runhook_hook_id_runbook_id_index", + "columns": [ + { + "expression": "hook_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "runbook_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "runhook_hook_id_hook_id_fk": { + "name": "runhook_hook_id_hook_id_fk", + "tableFrom": "runhook", + "tableTo": "hook", + "columnsFrom": ["hook_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "runhook_runbook_id_runbook_id_fk": { + "name": "runhook_runbook_id_runbook_id_fk", + "tableFrom": "runhook", + "tableTo": "runbook", + "columnsFrom": ["runbook_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.github_organization": { + "name": "github_organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "installation_id": { + "name": "installation_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "added_by_user_id": { + "name": "added_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'main'" + } + }, + "indexes": { + "unique_installation_workspace": { + "name": "unique_installation_workspace", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "github_organization_added_by_user_id_user_id_fk": { + "name": "github_organization_added_by_user_id_user_id_fk", + "tableFrom": "github_organization", + "tableTo": "user", + "columnsFrom": ["added_by_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "github_organization_workspace_id_workspace_id_fk": { + "name": "github_organization_workspace_id_workspace_id_fk", + "tableFrom": "github_organization", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.github_user": { + "name": "github_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "github_user_id": { + "name": "github_user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "github_username": { + "name": "github_username", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_user_user_id_user_id_fk": { + "name": "github_user_user_id_user_id_fk", + "tableFrom": "github_user", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment_resource_relationship": { + "name": "deployment_resource_relationship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_identifier": { + "name": "resource_identifier", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "deployment_resource_relationship_workspace_id_resource_identifier_index": { + "name": "deployment_resource_relationship_workspace_id_resource_identifier_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_resource_relationship_deployment_id_deployment_id_fk": { + "name": "deployment_resource_relationship_deployment_id_deployment_id_fk", + "tableFrom": "deployment_resource_relationship", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_resource_relationship_resource_identifier_workspace_id_resource_identifier_workspace_id_fk": { + "name": "deployment_resource_relationship_resource_identifier_workspace_id_resource_identifier_workspace_id_fk", + "tableFrom": "deployment_resource_relationship", + "tableTo": "resource", + "columnsFrom": ["resource_identifier", "workspace_id"], + "columnsTo": ["identifier", "workspace_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource": { + "name": "resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "resource_identifier_workspace_id_index": { + "name": "resource_identifier_workspace_id_index", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_provider_id_resource_provider_id_fk": { + "name": "resource_provider_id_resource_provider_id_fk", + "tableFrom": "resource", + "tableTo": "resource_provider", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "resource_workspace_id_workspace_id_fk": { + "name": "resource_workspace_id_workspace_id_fk", + "tableFrom": "resource", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource_metadata": { + "name": "resource_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "resource_metadata_key_resource_id_index": { + "name": "resource_metadata_key_resource_id_index", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_metadata_resource_id_resource_id_fk": { + "name": "resource_metadata_resource_id_resource_id_fk", + "tableFrom": "resource_metadata", + "tableTo": "resource", + "columnsFrom": ["resource_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource_relationship": { + "name": "resource_relationship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "source_id": { + "name": "source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "resource_relationship_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "resource_relationship_target_id_source_id_index": { + "name": "resource_relationship_target_id_source_id_index", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_relationship_source_id_resource_id_fk": { + "name": "resource_relationship_source_id_resource_id_fk", + "tableFrom": "resource_relationship", + "tableTo": "resource", + "columnsFrom": ["source_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "resource_relationship_target_id_resource_id_fk": { + "name": "resource_relationship_target_id_resource_id_fk", + "tableFrom": "resource_relationship", + "tableTo": "resource", + "columnsFrom": ["target_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource_schema": { + "name": "resource_schema", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "json_schema": { + "name": "json_schema", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "resource_schema_version_kind_workspace_id_index": { + "name": "resource_schema_version_kind_workspace_id_index", + "columns": [ + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_schema_workspace_id_workspace_id_fk": { + "name": "resource_schema_workspace_id_workspace_id_fk", + "tableFrom": "resource_schema", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource_variable": { + "name": "resource_variable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "resource_variable_resource_id_key_index": { + "name": "resource_variable_resource_id_key_index", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_variable_resource_id_resource_id_fk": { + "name": "resource_variable_resource_id_resource_id_fk", + "tableFrom": "resource_variable", + "tableTo": "resource", + "columnsFrom": ["resource_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource_view": { + "name": "resource_view", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "filter": { + "name": "filter", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "resource_view_workspace_id_workspace_id_fk": { + "name": "resource_view_workspace_id_workspace_id_fk", + "tableFrom": "resource_view", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource_provider": { + "name": "resource_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "resource_provider_workspace_id_name_index": { + "name": "resource_provider_workspace_id_name_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_provider_workspace_id_workspace_id_fk": { + "name": "resource_provider_workspace_id_workspace_id_fk", + "tableFrom": "resource_provider", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.resource_provider_google": { + "name": "resource_provider_google", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "resource_provider_id": { + "name": "resource_provider_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_ids": { + "name": "project_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "import_gke": { + "name": "import_gke", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "import_namespaces": { + "name": "import_namespaces", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "import_vcluster": { + "name": "import_vcluster", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "resource_provider_google_resource_provider_id_resource_provider_id_fk": { + "name": "resource_provider_google_resource_provider_id_resource_provider_id_fk", + "tableFrom": "resource_provider_google", + "tableTo": "resource_provider", + "columnsFrom": ["resource_provider_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.release": { + "name": "release", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "release_deployment_id_version_index": { + "name": "release_deployment_id_version_index", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "release_deployment_id_deployment_id_fk": { + "name": "release_deployment_id_deployment_id_fk", + "tableFrom": "release", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.release_channel": { + "name": "release_channel", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "release_filter": { + "name": "release_filter", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "NULL" + } + }, + "indexes": { + "release_channel_deployment_id_name_index": { + "name": "release_channel_deployment_id_name_index", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "release_channel_deployment_id_deployment_id_fk": { + "name": "release_channel_deployment_id_deployment_id_fk", + "tableFrom": "release_channel", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.release_dependency": { + "name": "release_dependency", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "release_id": { + "name": "release_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "release_filter": { + "name": "release_filter", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "NULL" + } + }, + "indexes": { + "release_dependency_release_id_deployment_id_index": { + "name": "release_dependency_release_id_deployment_id_index", + "columns": [ + { + "expression": "release_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "release_dependency_release_id_release_id_fk": { + "name": "release_dependency_release_id_release_id_fk", + "tableFrom": "release_dependency", + "tableTo": "release", + "columnsFrom": ["release_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "release_dependency_deployment_id_deployment_id_fk": { + "name": "release_dependency_deployment_id_deployment_id_fk", + "tableFrom": "release_dependency", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.release_job_trigger": { + "name": "release_job_trigger", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "release_job_trigger_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "caused_by_id": { + "name": "caused_by_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "release_id": { + "name": "release_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "release_job_trigger_job_id_job_id_fk": { + "name": "release_job_trigger_job_id_job_id_fk", + "tableFrom": "release_job_trigger", + "tableTo": "job", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "release_job_trigger_caused_by_id_user_id_fk": { + "name": "release_job_trigger_caused_by_id_user_id_fk", + "tableFrom": "release_job_trigger", + "tableTo": "user", + "columnsFrom": ["caused_by_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "release_job_trigger_release_id_release_id_fk": { + "name": "release_job_trigger_release_id_release_id_fk", + "tableFrom": "release_job_trigger", + "tableTo": "release", + "columnsFrom": ["release_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "release_job_trigger_resource_id_resource_id_fk": { + "name": "release_job_trigger_resource_id_resource_id_fk", + "tableFrom": "release_job_trigger", + "tableTo": "resource", + "columnsFrom": ["resource_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "release_job_trigger_environment_id_environment_id_fk": { + "name": "release_job_trigger_environment_id_environment_id_fk", + "tableFrom": "release_job_trigger", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "release_job_trigger_job_id_unique": { + "name": "release_job_trigger_job_id_unique", + "nullsNotDistinct": false, + "columns": ["job_id"] + } + } + }, + "public.release_metadata": { + "name": "release_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "release_id": { + "name": "release_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "release_metadata_key_release_id_index": { + "name": "release_metadata_key_release_id_index", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "release_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "release_metadata_release_id_release_id_fk": { + "name": "release_metadata_release_id_release_id_fk", + "tableFrom": "release_metadata", + "tableTo": "release", + "columnsFrom": ["release_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.system": { + "name": "system", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "system_workspace_id_slug_index": { + "name": "system_workspace_id_slug_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "system_workspace_id_workspace_id_fk": { + "name": "system_workspace_id_workspace_id_fk", + "tableFrom": "system", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.runbook": { + "name": "runbook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_id": { + "name": "system_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "job_agent_id": { + "name": "job_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "job_agent_config": { + "name": "job_agent_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + } + }, + "indexes": {}, + "foreignKeys": { + "runbook_system_id_system_id_fk": { + "name": "runbook_system_id_system_id_fk", + "tableFrom": "runbook", + "tableTo": "system", + "columnsFrom": ["system_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "runbook_job_agent_id_job_agent_id_fk": { + "name": "runbook_job_agent_id_job_agent_id_fk", + "tableFrom": "runbook", + "tableTo": "job_agent", + "columnsFrom": ["job_agent_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.runbook_job_trigger": { + "name": "runbook_job_trigger", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "runbook_id": { + "name": "runbook_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "runbook_job_trigger_job_id_job_id_fk": { + "name": "runbook_job_trigger_job_id_job_id_fk", + "tableFrom": "runbook_job_trigger", + "tableTo": "job", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "runbook_job_trigger_runbook_id_runbook_id_fk": { + "name": "runbook_job_trigger_runbook_id_runbook_id_fk", + "tableFrom": "runbook_job_trigger", + "tableTo": "runbook", + "columnsFrom": ["runbook_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "runbook_job_trigger_job_id_unique": { + "name": "runbook_job_trigger_job_id_unique", + "nullsNotDistinct": false, + "columns": ["job_id"] + } + } + }, + "public.team": { + "name": "team", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "team_workspace_id_workspace_id_fk": { + "name": "team_workspace_id_workspace_id_fk", + "tableFrom": "team", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.team_member": { + "name": "team_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "team_member_team_id_user_id_index": { + "name": "team_member_team_id_user_id_index", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_member_team_id_team_id_fk": { + "name": "team_member_team_id_team_id_fk", + "tableFrom": "team_member", + "tableTo": "team", + "columnsFrom": ["team_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_member_user_id_user_id_fk": { + "name": "team_member_user_id_user_id_fk", + "tableFrom": "team_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.job": { + "name": "job", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_agent_id": { + "name": "job_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "job_agent_config": { + "name": "job_agent_config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "job_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "job_reason", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'policy_passing'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "job_job_agent_id_job_agent_id_fk": { + "name": "job_job_agent_id_job_agent_id_fk", + "tableFrom": "job", + "tableTo": "job_agent", + "columnsFrom": ["job_agent_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.job_metadata": { + "name": "job_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "job_metadata_key_job_id_index": { + "name": "job_metadata_key_job_id_index", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_metadata_job_id_job_id_fk": { + "name": "job_metadata_job_id_job_id_fk", + "tableFrom": "job_metadata", + "tableTo": "job", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.job_variable": { + "name": "job_variable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "job_variable_job_id_key_index": { + "name": "job_variable_job_id_key_index", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_variable_job_id_job_id_fk": { + "name": "job_variable_job_id_job_id_fk", + "tableFrom": "job_variable", + "tableTo": "job", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "google_service_account_email": { + "name": "google_service_account_email", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_slug_unique": { + "name": "workspace_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + } + } + }, + "public.workspace_email_domain_matching": { + "name": "workspace_email_domain_matching", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "verification_code": { + "name": "verification_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_email": { + "name": "verification_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_email_domain_matching_workspace_id_domain_index": { + "name": "workspace_email_domain_matching_workspace_id_domain_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_email_domain_matching_workspace_id_workspace_id_fk": { + "name": "workspace_email_domain_matching_workspace_id_workspace_id_fk", + "tableFrom": "workspace_email_domain_matching", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_email_domain_matching_role_id_role_id_fk": { + "name": "workspace_email_domain_matching_role_id_role_id_fk", + "tableFrom": "workspace_email_domain_matching", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.variable_set": { + "name": "variable_set", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_id": { + "name": "system_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "variable_set_system_id_system_id_fk": { + "name": "variable_set_system_id_system_id_fk", + "tableFrom": "variable_set", + "tableTo": "system", + "columnsFrom": ["system_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.variable_set_environment": { + "name": "variable_set_environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variable_set_id": { + "name": "variable_set_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "variable_set_environment_variable_set_id_variable_set_id_fk": { + "name": "variable_set_environment_variable_set_id_variable_set_id_fk", + "tableFrom": "variable_set_environment", + "tableTo": "variable_set", + "columnsFrom": ["variable_set_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "variable_set_environment_environment_id_environment_id_fk": { + "name": "variable_set_environment_environment_id_environment_id_fk", + "tableFrom": "variable_set_environment", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.variable_set_value": { + "name": "variable_set_value", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "variable_set_id": { + "name": "variable_set_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "variable_set_value_variable_set_id_key_index": { + "name": "variable_set_value_variable_set_id_key_index", + "columns": [ + { + "expression": "variable_set_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "variable_set_value_variable_set_id_variable_set_id_fk": { + "name": "variable_set_value_variable_set_id_variable_set_id_fk", + "tableFrom": "variable_set_value", + "tableTo": "variable_set", + "columnsFrom": ["variable_set_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.workspace_invite_token": { + "name": "workspace_invite_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_invite_token_role_id_role_id_fk": { + "name": "workspace_invite_token_role_id_role_id_fk", + "tableFrom": "workspace_invite_token", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_invite_token_workspace_id_workspace_id_fk": { + "name": "workspace_invite_token_workspace_id_workspace_id_fk", + "tableFrom": "workspace_invite_token", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_invite_token_created_by_user_id_fk": { + "name": "workspace_invite_token_created_by_user_id_fk", + "tableFrom": "workspace_invite_token", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_invite_token_token_unique": { + "name": "workspace_invite_token_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + } + }, + "public.resource_metadata_group": { + "name": "resource_metadata_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "keys": { + "name": "keys", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "include_null_combinations": { + "name": "include_null_combinations", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "resource_metadata_group_workspace_id_workspace_id_fk": { + "name": "resource_metadata_group_workspace_id_workspace_id_fk", + "tableFrom": "resource_metadata_group", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.runbook_variable": { + "name": "runbook_variable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "runbook_id": { + "name": "runbook_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "schema": { + "name": "schema", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "runbook_variable_runbook_id_key_index": { + "name": "runbook_variable_runbook_id_key_index", + "columns": [ + { + "expression": "runbook_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "runbook_variable_runbook_id_runbook_id_fk": { + "name": "runbook_variable_runbook_id_runbook_id_fk", + "tableFrom": "runbook_variable", + "tableTo": "runbook", + "columnsFrom": ["runbook_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.entity_role": { + "name": "entity_role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "entity_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "scope_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "entity_role_role_id_entity_type_entity_id_scope_id_scope_type_index": { + "name": "entity_role_role_id_entity_type_entity_id_scope_id_scope_type_index", + "columns": [ + { + "expression": "role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "entity_role_role_id_role_id_fk": { + "name": "entity_role_role_id_role_id_fk", + "tableFrom": "entity_role", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.role": { + "name": "role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "role_workspace_id_workspace_id_fk": { + "name": "role_workspace_id_workspace_id_fk", + "tableFrom": "role", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.role_permission": { + "name": "role_permission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "role_permission_role_id_permission_index": { + "name": "role_permission_role_id_permission_index", + "columns": [ + { + "expression": "role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "role_permission_role_id_role_id_fk": { + "name": "role_permission_role_id_role_id_fk", + "tableFrom": "role_permission", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.job_agent": { + "name": "job_agent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + } + }, + "indexes": { + "job_agent_workspace_id_name_index": { + "name": "job_agent_workspace_id_name_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_agent_workspace_id_workspace_id_fk": { + "name": "job_agent_workspace_id_workspace_id_fk", + "tableFrom": "job_agent", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "public.environment_policy_approval_requirement": { + "name": "environment_policy_approval_requirement", + "schema": "public", + "values": ["manual", "automatic"] + }, + "public.approval_status_type": { + "name": "approval_status_type", + "schema": "public", + "values": ["pending", "approved", "rejected"] + }, + "public.concurrency_type": { + "name": "concurrency_type", + "schema": "public", + "values": ["all", "some"] + }, + "public.environment_policy_deployment_success_type": { + "name": "environment_policy_deployment_success_type", + "schema": "public", + "values": ["all", "some", "optional"] + }, + "public.recurrence_type": { + "name": "recurrence_type", + "schema": "public", + "values": ["hourly", "daily", "weekly", "monthly"] + }, + "public.release_sequencing_type": { + "name": "release_sequencing_type", + "schema": "public", + "values": ["wait", "cancel"] + }, + "public.resource_relationship_type": { + "name": "resource_relationship_type", + "schema": "public", + "values": ["associated_with", "depends_on"] + }, + "public.release_job_trigger_type": { + "name": "release_job_trigger_type", + "schema": "public", + "values": [ + "new_release", + "new_resource", + "resource_changed", + "api", + "redeploy", + "force_deploy", + "new_environment", + "variable_changed" + ] + }, + "public.job_reason": { + "name": "job_reason", + "schema": "public", + "values": [ + "policy_passing", + "policy_override", + "env_policy_override", + "config_policy_override" + ] + }, + "public.job_status": { + "name": "job_status", + "schema": "public", + "values": [ + "completed", + "cancelled", + "skipped", + "in_progress", + "action_required", + "pending", + "failure", + "invalid_job_agent", + "invalid_integration", + "external_run_not_found" + ] + }, + "public.entity_type": { + "name": "entity_type", + "schema": "public", + "values": ["user", "team"] + }, + "public.scope_type": { + "name": "scope_type", + "schema": "public", + "values": [ + "release", + "releaseChannel", + "resource", + "resourceProvider", + "resourceMetadataGroup", + "workspace", + "environment", + "environmentPolicy", + "deploymentVariable", + "variableSet", + "system", + "deployment", + "job", + "jobAgent", + "runbook", + "resourceView" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index 41e87796..1fad0254 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -288,6 +288,13 @@ "when": 1732077031948, "tag": "0040_whole_layla_miller", "breakpoints": true + }, + { + "idx": 41, + "version": "7", + "when": 1732139824120, + "tag": "0041_smooth_tarantula", + "breakpoints": true } ] } diff --git a/packages/db/src/schema/job.ts b/packages/db/src/schema/job.ts index 692bc765..88b9f7cd 100644 --- a/packages/db/src/schema/job.ts +++ b/packages/db/src/schema/job.ts @@ -11,6 +11,7 @@ import { exists, gt, gte, + isNull, like, lt, lte, @@ -218,7 +219,8 @@ const buildCondition = (tx: Tx, cond: JobCondition): SQL => { if (cond.type === JobFilterType.Environment) return eq(environment.id, cond.value); if (cond.type === FilterType.Version) return buildVersionCondition(cond); - if (cond.type === JobFilterType.JobTarget) return eq(resource.id, cond.value); + if (cond.type === JobFilterType.JobTarget) + return and(eq(resource.id, cond.value), isNull(resource.deletedAt))!; if (cond.type === JobFilterType.Release) return eq(release.id, cond.value); const subCon = cond.conditions.map((c) => buildCondition(tx, c)); diff --git a/packages/db/src/schema/resource-agent.ts b/packages/db/src/schema/resource-agent.ts deleted file mode 100644 index d129840a..00000000 --- a/packages/db/src/schema/resource-agent.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { pgTable, uuid } from "drizzle-orm/pg-core"; - -import { resource } from "./resource.js"; - -export const resourceSession = pgTable("resource_session", { - id: uuid("id").primaryKey(), - resourceId: uuid("resource_id") - .references(() => resource.id, { onDelete: "cascade" }) - .notNull(), -}); diff --git a/packages/db/src/schema/resource-group.ts b/packages/db/src/schema/resource-group.ts index b729a833..9f6fdc1c 100644 --- a/packages/db/src/schema/resource-group.ts +++ b/packages/db/src/schema/resource-group.ts @@ -21,12 +21,8 @@ export const resourceMetadataGroup = pgTable("resource_metadata_group", { export const createResourceMetadataGroup = createInsertSchema( resourceMetadataGroup, ) - .omit({ - id: true, - }) - .extend({ - keys: z.array(z.string()), - }); + .omit({ id: true }) + .extend({ keys: z.array(z.string()) }); export const updateResourceMetadataGroup = createResourceMetadataGroup.partial(); export type ResourceMetadataGroup = InferSelectModel< diff --git a/packages/db/src/schema/resource-session.ts b/packages/db/src/schema/resource-session.ts deleted file mode 100644 index 0c4743b0..00000000 --- a/packages/db/src/schema/resource-session.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { pgTable, uuid } from "drizzle-orm/pg-core"; - -import { user } from "./auth.js"; -import { resource } from "./resource.js"; - -export const resourceSession = pgTable("resource_session", { - id: uuid("id").primaryKey(), - resourceId: uuid("resource_id") - .references(() => resource.id) - .notNull(), - createdBy: uuid("created_by_id") - .references(() => user.id) - .notNull(), -}); diff --git a/packages/db/src/schema/resource.ts b/packages/db/src/schema/resource.ts index 821dc99f..7c5fb116 100644 --- a/packages/db/src/schema/resource.ts +++ b/packages/db/src/schema/resource.ts @@ -61,6 +61,7 @@ export const resource = pgTable( updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate( () => new Date(), ), + deletedAt: timestamp("deleted_at", { withTimezone: true }), }, (t) => ({ uniq: uniqueIndex().on(t.identifier, t.workspaceId) }), ); diff --git a/packages/job-dispatch/src/__test__/job-variables-deployment.test.ts b/packages/job-dispatch/src/__test__/job-variables-deployment.test.ts index 7d53cc44..e36e90c6 100644 --- a/packages/job-dispatch/src/__test__/job-variables-deployment.test.ts +++ b/packages/job-dispatch/src/__test__/job-variables-deployment.test.ts @@ -63,6 +63,7 @@ const target: SCHEMA.Resource = { providerId: "0", lockedAt: null, createdAt: new Date(), + deletedAt: null, }; const release: SCHEMA.Release = { diff --git a/packages/job-dispatch/src/environment-creation.ts b/packages/job-dispatch/src/environment-creation.ts index bf49cb3b..d3f410ae 100644 --- a/packages/job-dispatch/src/environment-creation.ts +++ b/packages/job-dispatch/src/environment-creation.ts @@ -1,7 +1,7 @@ import type { Tx } from "@ctrlplane/db"; import { isPresent } from "ts-is-present"; -import { and, desc, eq, takeFirstOrNull } from "@ctrlplane/db"; +import { and, desc, eq, isNull, takeFirstOrNull } from "@ctrlplane/db"; import * as SCHEMA from "@ctrlplane/db/schema"; import { dispatchReleaseJobTriggers } from "./job-dispatch.js"; @@ -44,6 +44,7 @@ export const createJobsForNewEnvironment = async ( and( eq(SCHEMA.resource.workspaceId, workspaceId), SCHEMA.resourceMatchesMetadata(db, resourceFilter), + isNull(SCHEMA.resource.deletedAt), ), ); if (resources.length === 0) return; diff --git a/packages/job-dispatch/src/events/triggers/deployment-deleted.ts b/packages/job-dispatch/src/events/triggers/deployment-deleted.ts index 89053e05..6d065f18 100644 --- a/packages/job-dispatch/src/events/triggers/deployment-deleted.ts +++ b/packages/job-dispatch/src/events/triggers/deployment-deleted.ts @@ -2,7 +2,7 @@ import type { HookEvent } from "@ctrlplane/validators/events"; import type { ResourceCondition } from "@ctrlplane/validators/resources"; import { isPresent } from "ts-is-present"; -import { eq, isNotNull } from "@ctrlplane/db"; +import { and, eq, isNotNull, isNull } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import * as SCHEMA from "@ctrlplane/db/schema"; import { ComparisonOperator } from "@ctrlplane/validators/conditions"; @@ -31,7 +31,10 @@ export const getEventsForDeploymentDeleted = async ( }; const resources = await db.query.resource.findMany({ - where: SCHEMA.resourceMatchesMetadata(db, systemFilter), + where: and( + SCHEMA.resourceMatchesMetadata(db, systemFilter), + isNull(SCHEMA.resource.deletedAt), + ), }); return resources.map((resource) => ({ diff --git a/packages/job-dispatch/src/events/triggers/environment-deleted.ts b/packages/job-dispatch/src/events/triggers/environment-deleted.ts index c43e67ec..53e0b8fb 100644 --- a/packages/job-dispatch/src/events/triggers/environment-deleted.ts +++ b/packages/job-dispatch/src/events/triggers/environment-deleted.ts @@ -2,7 +2,7 @@ import type { HookEvent } from "@ctrlplane/validators/events"; import type { ResourceCondition } from "@ctrlplane/validators/resources"; import { isPresent } from "ts-is-present"; -import { and, eq, inArray, isNotNull, ne } from "@ctrlplane/db"; +import { and, eq, inArray, isNotNull, isNull, ne } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import * as SCHEMA from "@ctrlplane/db/schema"; import { ComparisonOperator } from "@ctrlplane/validators/conditions"; @@ -15,7 +15,12 @@ export const getEventsForEnvironmentDeleted = async ( const resources = await db .select() .from(SCHEMA.resource) - .where(SCHEMA.resourceMatchesMetadata(db, environment.resourceFilter)); + .where( + and( + SCHEMA.resourceMatchesMetadata(db, environment.resourceFilter), + isNull(SCHEMA.resource.deletedAt), + ), + ); if (resources.length === 0) return []; const checks = and( @@ -47,6 +52,7 @@ export const getEventsForEnvironmentDeleted = async ( .where( and( SCHEMA.resourceMatchesMetadata(db, removedFromSystemFilter), + isNull(SCHEMA.resource.deletedAt), inArray( SCHEMA.resource.id, resources.map((r) => r.id), diff --git a/packages/job-dispatch/src/events/triggers/resource-deleted.ts b/packages/job-dispatch/src/events/triggers/resource-deleted.ts index cf54ba7f..e1301c12 100644 --- a/packages/job-dispatch/src/events/triggers/resource-deleted.ts +++ b/packages/job-dispatch/src/events/triggers/resource-deleted.ts @@ -2,7 +2,7 @@ import type { HookEvent } from "@ctrlplane/validators/events"; import type { ResourceCondition } from "@ctrlplane/validators/resources"; import { isPresent } from "ts-is-present"; -import { eq, isNotNull } from "@ctrlplane/db"; +import { and, eq, isNotNull, isNull } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import * as SCHEMA from "@ctrlplane/db/schema"; import { ComparisonOperator } from "@ctrlplane/validators/conditions"; @@ -37,7 +37,10 @@ export const getEventsForResourceDeleted = async ( }; const matchedResource = await db.query.resource.findFirst({ - where: SCHEMA.resourceMatchesMetadata(db, systemFilter), + where: and( + SCHEMA.resourceMatchesMetadata(db, systemFilter), + isNull(SCHEMA.resource.deletedAt), + ), }); if (matchedResource == null) return []; diff --git a/packages/job-dispatch/src/job-variables-deployment/utils.ts b/packages/job-dispatch/src/job-variables-deployment/utils.ts index 18493826..5e08462e 100644 --- a/packages/job-dispatch/src/job-variables-deployment/utils.ts +++ b/packages/job-dispatch/src/job-variables-deployment/utils.ts @@ -2,7 +2,7 @@ import type { Tx } from "@ctrlplane/db"; import type { ResourceCondition } from "@ctrlplane/validators/resources"; import { isPresent } from "ts-is-present"; -import { and, eq, takeFirstOrNull } from "@ctrlplane/db"; +import { and, eq, isNull, takeFirstOrNull } from "@ctrlplane/db"; import * as SCHEMA from "@ctrlplane/db/schema"; export const getJob = (tx: Tx, jobId: string) => @@ -30,7 +30,9 @@ export const getTarget = (tx: Tx, targetId: string) => tx .select() .from(SCHEMA.resource) - .where(eq(SCHEMA.resource.id, targetId)) + .where( + and(eq(SCHEMA.resource.id, targetId), isNull(SCHEMA.resource.deletedAt)), + ) .then(takeFirstOrNull); export const getEnvironment = (tx: Tx, environmentId: string) => @@ -72,6 +74,7 @@ export const getMatchedTarget = ( and( eq(SCHEMA.resource.id, targetId), SCHEMA.resourceMatchesMetadata(tx, targetFilter), + isNull(SCHEMA.resource.deletedAt), ), ) .then(takeFirstOrNull); diff --git a/packages/job-dispatch/src/lock-checker.ts b/packages/job-dispatch/src/lock-checker.ts index e82bc50e..b67e02c4 100644 --- a/packages/job-dispatch/src/lock-checker.ts +++ b/packages/job-dispatch/src/lock-checker.ts @@ -18,6 +18,7 @@ export const isPassingLockingPolicy: ReleaseIdPolicyChecker = ( releaseJobTriggers.map((t) => t.id), ), isNull(resource.lockedAt), + isNull(resource.deletedAt), ), ) .then((data) => data.map((d) => d.release_job_trigger)); diff --git a/packages/job-dispatch/src/release-job-trigger.ts b/packages/job-dispatch/src/release-job-trigger.ts index 85c923fd..42b2cfd2 100644 --- a/packages/job-dispatch/src/release-job-trigger.ts +++ b/packages/job-dispatch/src/release-job-trigger.ts @@ -145,6 +145,7 @@ class ReleaseJobTriggerBuilder { resourceMatchesMetadata(this.tx, resourceFilter), eq(resource.workspaceId, workspaceId), isNull(resource.lockedAt), + isNull(resource.deletedAt), this.resourceIds && inArray(resource.id, this.resourceIds), ), ); diff --git a/packages/job-dispatch/src/resource.ts b/packages/job-dispatch/src/resource.ts index 390a134e..8f986b48 100644 --- a/packages/job-dispatch/src/resource.ts +++ b/packages/job-dispatch/src/resource.ts @@ -8,14 +8,17 @@ import { eq, inArray, isNotNull, + isNull, or, } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import { + deploymentResourceRelationship, environment, resource, resourceMatchesMetadata, resourceMetadata, + resourceRelationship, resourceVariable, system, } from "@ctrlplane/db/schema"; @@ -27,8 +30,13 @@ import { dispatchJobsForNewResources } from "./new-resource.js"; const log = logger.child({ label: "upsert-resources" }); +const isNotDeleted = isNull(resource.deletedAt); + const getExistingResourcesForProvider = (db: Tx, providerId: string) => - db.select().from(resource).where(eq(resource.providerId, providerId)); + db + .select() + .from(resource) + .where(and(eq(resource.providerId, providerId), isNotDeleted)); const dispatchNewResources = async (db: Tx, newResources: Resource[]) => { const [firstResource] = newResources; @@ -55,6 +63,7 @@ const dispatchNewResources = async (db: Tx, newResources: Resource[]) => { and( inArray(resource.id, resourceIds), resourceMatchesMetadata(db, env.resourceFilter), + isNotDeleted, ), ) .then((tgs) => { @@ -240,6 +249,7 @@ export const upsertResources = async ( and( eq(resource.workspaceId, r.workspaceId), eq(resource.identifier, r.identifier), + isNotDeleted, ), ), ), @@ -265,6 +275,7 @@ export const upsertResources = async ( "config", ]), updatedAt: new Date(), + deletedAt: null, }, }) .returning() @@ -338,6 +349,26 @@ export const upsertResources = async ( } }; +const deleteObjectsAssociatedWithResource = (tx: Tx, resource: Resource) => + Promise.all([ + tx + .delete(resourceRelationship) + .where( + or( + eq(resourceRelationship.sourceId, resource.id), + eq(resourceRelationship.targetId, resource.id), + ), + ), + tx + .delete(deploymentResourceRelationship) + .where( + eq( + deploymentResourceRelationship.resourceIdentifier, + resource.identifier, + ), + ), + ]); + /** * Delete resources from the database. * @@ -351,5 +382,14 @@ export const deleteResources = async (tx: Tx, resources: Resource[]) => { const events = await eventsPromises.then((res) => res.flat()); await Promise.all(events.map(handleEvent)); const resourceIds = resources.map((r) => r.id); - await tx.delete(resource).where(inArray(resource.id, resourceIds)); + const deleteAssociatedObjects = Promise.all( + resources.map((r) => deleteObjectsAssociatedWithResource(tx, r)), + ); + await Promise.all([ + deleteAssociatedObjects, + tx + .update(resource) + .set({ deletedAt: new Date() }) + .where(inArray(resource.id, resourceIds)), + ]); }; From 7f3c290029af5fd0979aea1b7189659f9c9efe05 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Wed, 20 Nov 2024 16:41:10 -0800 Subject: [PATCH 2/2] some cleanup --- .../api/v1/resources/[resourceId]/route.ts | 5 ++-- .../identifier/[identifier]/route.ts | 9 +++--- .../api/src/router/target-metadata-group.ts | 11 ++++++-- packages/api/src/router/target-provider.ts | 28 ++++++++++++++----- packages/auth/src/utils/rbac.ts | 6 ++-- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts b/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts index 50792298..1c7a3eca 100644 --- a/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts +++ b/apps/webservice/src/app/api/v1/resources/[resourceId]/route.ts @@ -22,10 +22,9 @@ export const GET = request() }), ) .handle(async ({ db }, { params }: { params: { resourceId: string } }) => { - const isResource = eq(schema.resource.id, params.resourceId); - const isNotDeleted = isNull(schema.resource.deletedAt); + // we don't check deletedAt as we may be querying for soft-deleted resources const data = await db.query.resource.findFirst({ - where: and(isResource, isNotDeleted), + where: eq(schema.resource.id, params.resourceId), with: { metadata: true, variables: true, provider: true }, }); diff --git a/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts b/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts index 8fda6ff8..0b8a3873 100644 --- a/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts +++ b/apps/webservice/src/app/api/v1/workspaces/[workspaceId]/resources/identifier/[identifier]/route.ts @@ -3,6 +3,7 @@ import { NextResponse } from "next/server"; import { and, eq, isNull } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import * as schema from "@ctrlplane/db/schema"; +import { deleteResources } from "@ctrlplane/job-dispatch"; import { Permission } from "@ctrlplane/validators/auth"; import { authn, authz } from "~/app/api/v1/auth"; @@ -14,11 +15,11 @@ export const GET = request() authz(async ({ can, extra }) => { const { workspaceId, identifier } = extra; + // we don't check deletedAt as we may be querying for soft-deleted resources const resource = await db.query.resource.findFirst({ where: and( eq(schema.resource.workspaceId, workspaceId), eq(schema.resource.identifier, identifier), - isNull(schema.resource.deletedAt), ), }); @@ -30,11 +31,11 @@ export const GET = request() ) .handle( async (_, { params }) => { + // we don't check deletedAt as we may be querying for soft-deleted resources const data = await db.query.resource.findFirst({ where: and( eq(schema.resource.workspaceId, params.workspaceId), eq(schema.resource.identifier, params.identifier), - isNull(schema.resource.deletedAt), ), with: { metadata: true, @@ -95,9 +96,7 @@ export const DELETE = request() ); } - await db - .delete(schema.resource) - .where(eq(schema.resource.id, resource.id)); + await deleteResources(db, [resource]); return NextResponse.json({ success: true }); }, diff --git a/packages/api/src/router/target-metadata-group.ts b/packages/api/src/router/target-metadata-group.ts index e5391315..62641920 100644 --- a/packages/api/src/router/target-metadata-group.ts +++ b/packages/api/src/router/target-metadata-group.ts @@ -7,6 +7,7 @@ import { count, eq, inArray, + isNull, sql, takeFirst, takeFirstOrNull, @@ -49,6 +50,7 @@ export const resourceMetadataGroupRouter = createTRPCRouter({ ) .where( and( + isNull(resource.deletedAt), eq(resource.workspaceId, resourceMetadataGroup.workspaceId), sql`${resourceMetadata.key} = ANY(${resourceMetadataGroup.keys})`, ), @@ -81,7 +83,7 @@ export const resourceMetadataGroupRouter = createTRPCRouter({ resources: count(), }) .from(resource) - .where(eq(resource.workspaceId, input)) + .where(and(eq(resource.workspaceId, input), isNull(resource.deletedAt))) .then(takeFirst) .then((row) => row.resources); @@ -151,7 +153,12 @@ export const resourceMetadataGroupRouter = createTRPCRouter({ inArray(resourceMetadata.key, group.keys), ), ) - .where(eq(resource.workspaceId, group.workspaceId)) + .where( + and( + eq(resource.workspaceId, group.workspaceId), + isNull(resource.deletedAt), + ), + ) .groupBy(resource.id); const resourceMetadataAgg = group.includeNullCombinations diff --git a/packages/api/src/router/target-provider.ts b/packages/api/src/router/target-provider.ts index a7ca8cbd..dfc59c43 100644 --- a/packages/api/src/router/target-provider.ts +++ b/packages/api/src/router/target-provider.ts @@ -1,7 +1,15 @@ import ms from "ms"; import { z } from "zod"; -import { eq, inArray, sql, takeFirst, takeFirstOrNull } from "@ctrlplane/db"; +import { + and, + eq, + inArray, + isNull, + sql, + takeFirst, + takeFirstOrNull, +} from "@ctrlplane/db"; import { createResourceProvider, createResourceProviderGoogle, @@ -43,9 +51,12 @@ export const resourceProviderRouter = createTRPCRouter({ }) .from(resource) .where( - inArray( - resource.providerId, - providers.map((p) => p.resource_provider.id), + and( + inArray( + resource.providerId, + providers.map((p) => p.resource_provider.id), + ), + isNull(resource.deletedAt), ), ) .groupBy(resource.providerId); @@ -59,9 +70,12 @@ export const resourceProviderRouter = createTRPCRouter({ }) .from(resource) .where( - inArray( - resource.providerId, - providers.map((p) => p.resource_provider.id), + and( + inArray( + resource.providerId, + providers.map((p) => p.resource_provider.id), + ), + isNull(resource.deletedAt), ), ) .groupBy(resource.providerId, resource.kind, resource.version) diff --git a/packages/auth/src/utils/rbac.ts b/packages/auth/src/utils/rbac.ts index 55c7a9e5..d699f388 100644 --- a/packages/auth/src/utils/rbac.ts +++ b/packages/auth/src/utils/rbac.ts @@ -197,10 +197,8 @@ const getResourceScopes = async (id: string) => { .select() .from(workspace) .innerJoin(resource, eq(resource.workspaceId, workspace.id)) - .where(and(eq(resource.id, id), isNull(resource.deletedAt))) - .then(takeFirstOrNull); - - if (result == null) return []; + .where(eq(resource.id, id)) + .then(takeFirst); return [ { type: "resource" as const, id: result.resource.id },