Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const approvalRouter = createTRPCRouter({
const { deploymentVersionId, environmentId, status, reason } = input;

const record = await ctx.db
.insert(SCHEMA.policyRuleAnyApprovalRecord)
.insert(SCHEMA.deploymentVersionApprovalRecord)
.values({
deploymentVersionId,
userId: ctx.session.user.id,
Expand Down
4 changes: 4 additions & 0 deletions packages/db/src/schema/rbac.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export const rolePermission = pgTable(
export const entityType = pgEnum("entity_type", ["user", "team"]);
export const entityTypeSchema = z.enum(entityType.enumValues);
export type EntityType = z.infer<typeof entityTypeSchema>;
export enum EntityTypeEnum {
User = "user",
Team = "team",
}

export const scopeType = pgEnum("scope_type", [
"deploymentVersion",
Expand Down
38 changes: 1 addition & 37 deletions packages/db/src/schema/rules/approval-any.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import type { InferSelectModel } from "drizzle-orm";
import { integer, pgTable, uniqueIndex } from "drizzle-orm/pg-core";
import { integer, pgTable } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";

import {
baseApprovalRecordFields,
baseApprovalRecordValidationFields,
} from "./approval-base.js";
import {
basePolicyRuleFields,
basePolicyRuleValidationFields,
Expand All @@ -22,18 +18,6 @@ export const policyRuleAnyApproval = pgTable("policy_rule_any_approval", {
.default(1),
});

// Approval records specific to any approval rules
export const policyRuleAnyApprovalRecord = pgTable(
"policy_rule_any_approval_record",
baseApprovalRecordFields,
(t) => ({
uniqueRuleIdUserId: uniqueIndex("unique_rule_id_user_id").on(
t.deploymentVersionId,
t.userId,
),
}),
);

// Validation schemas
export const policyRuleAnyApprovalInsertSchema = createInsertSchema(
policyRuleAnyApproval,
Expand All @@ -43,40 +27,20 @@ export const policyRuleAnyApprovalInsertSchema = createInsertSchema(
},
).omit({ id: true, createdAt: true });

export const policyRuleAnyApprovalRecordInsertSchema = createInsertSchema(
policyRuleAnyApprovalRecord,
baseApprovalRecordValidationFields,
).omit({ id: true, createdAt: true, updatedAt: true });

// Export create schemas
export const createPolicyRuleAnyApproval = policyRuleAnyApprovalInsertSchema;
export type CreatePolicyRuleAnyApproval = z.infer<
typeof createPolicyRuleAnyApproval
>;

export const createPolicyRuleAnyApprovalRecord =
policyRuleAnyApprovalRecordInsertSchema;
export type CreatePolicyRuleAnyApprovalRecord = z.infer<
typeof createPolicyRuleAnyApprovalRecord
>;

// Export update schemas
export const updatePolicyRuleAnyApproval =
policyRuleAnyApprovalInsertSchema.partial();
export type UpdatePolicyRuleAnyApproval = z.infer<
typeof updatePolicyRuleAnyApproval
>;

export const updatePolicyRuleAnyApprovalRecord =
policyRuleAnyApprovalRecordInsertSchema.partial();
export type UpdatePolicyRuleAnyApprovalRecord = z.infer<
typeof updatePolicyRuleAnyApprovalRecord
>;

// Export model types
export type PolicyRuleAnyApproval = InferSelectModel<
typeof policyRuleAnyApproval
>;
export type PolicyRuleAnyApprovalRecord = InferSelectModel<
typeof policyRuleAnyApprovalRecord
>;
56 changes: 0 additions & 56 deletions packages/db/src/schema/rules/approval-base.ts

This file was deleted.

69 changes: 69 additions & 0 deletions packages/db/src/schema/rules/approval-record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { sql } from "drizzle-orm";
import {
pgEnum,
pgTable,
text,
timestamp,
uniqueIndex,
uuid,
} from "drizzle-orm/pg-core";
import { z } from "zod";

import { user } from "../auth.js";

export const approvalStatus = pgEnum("approval_status", [
"approved",
"rejected",
]);

export const deploymentVersionApprovalRecord = pgTable(
"deployment_version_approval_record",
{
id: uuid("id").primaryKey().defaultRandom(),

// Link to the deployment version being approved
deploymentVersionId: uuid("deployment_version_id").notNull(),

// User who performed the approval/rejection action
userId: uuid("user_id")
.references(() => user.id)
.notNull(),

Comment on lines +25 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing FK to the deployment-version table may break referential integrity

deploymentVersionId is declared as a bare UUID. Without a .references(() => deploymentVersion.id …) clause the DB won’t enforce that the referenced deployment version actually exists, and cascaded deletes/updates are impossible.

-    deploymentVersionId: uuid("deployment_version_id").notNull(),
+    deploymentVersionId: uuid("deployment_version_id")
+      .notNull()
+      .references(() => deploymentVersion.id, { onDelete: "cascade" }),

(you’ll need to import deploymentVersion from its schema module at the top).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
deploymentVersionId: uuid("deployment_version_id").notNull(),
// User who performed the approval/rejection action
userId: uuid("user_id")
.references(() => user.id)
.notNull(),
deploymentVersionId: uuid("deployment_version_id")
.notNull()
.references(() => deploymentVersion.id, { onDelete: "cascade" }),
// User who performed the approval/rejection action
userId: uuid("user_id")
.references(() => user.id)
.notNull(),

// Status of this approval
status: approvalStatus("status").notNull(),

// Timestamp of when the approval was performed
approvedAt: timestamp("approved_at", { withTimezone: true }).default(
sql`NULL`,
),

// Reason provided by approver
reason: text("reason"),

createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
.defaultNow(),

updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(
() => new Date(),
),
},
(t) => ({
uniqueDeploymentVersionIdUserId: uniqueIndex(
"unique_deployment_version_id_user_id",
).on(t.deploymentVersionId, t.userId),
}),
);

// Base validation fields for approval records
export const baseApprovalRecordValidationFields = {
deploymentVersionId: z.string().uuid(),
userId: z.string().uuid(),
status: z.enum(approvalStatus.enumValues),
reason: z.string().optional(),
};
Comment on lines +58 to +64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validation schema allows undefined but not null for reason

DB column reason is nullable; inserting null directly will violate Zod.

-  reason: z.string().optional(),
+  reason: z.string().nullable().optional(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Base validation fields for approval records
export const baseApprovalRecordValidationFields = {
deploymentVersionId: z.string().uuid(),
userId: z.string().uuid(),
status: z.enum(approvalStatus.enumValues),
reason: z.string().optional(),
};
// Base validation fields for approval records
export const baseApprovalRecordValidationFields = {
deploymentVersionId: z.string().uuid(),
userId: z.string().uuid(),
status: z.enum(approvalStatus.enumValues),
reason: z.string().nullable().optional(),
};


export enum ApprovalStatus {
Approved = "approved",
Rejected = "rejected",
}
40 changes: 0 additions & 40 deletions packages/db/src/schema/rules/approval-role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";

import { role } from "../rbac.js";
import {
baseApprovalRecordFields,
baseApprovalRecordValidationFields,
} from "./approval-base.js";
import {
basePolicyRuleFields,
basePolicyRuleValidationFields,
Expand All @@ -28,19 +24,6 @@ export const policyRuleRoleApproval = pgTable("policy_rule_role_approval", {
.default(1),
});

// Approval records specific to role approval rules
export const policyRuleRoleApprovalRecord = pgTable(
"policy_rule_role_approval_record",
{
...baseApprovalRecordFields,

// Link to the role approval rule
ruleId: uuid("rule_id")
.notNull()
.references(() => policyRuleRoleApproval.id, { onDelete: "cascade" }),
},
);

// Validation schemas
export const policyRuleRoleApprovalInsertSchema = createInsertSchema(
policyRuleRoleApproval,
Expand All @@ -51,43 +34,20 @@ export const policyRuleRoleApprovalInsertSchema = createInsertSchema(
},
).omit({ id: true, createdAt: true });

export const policyRuleRoleApprovalRecordInsertSchema = createInsertSchema(
policyRuleRoleApprovalRecord,
{
...baseApprovalRecordValidationFields,
ruleId: z.string().uuid(),
},
).omit({ id: true, createdAt: true, updatedAt: true });

// Export create schemas
export const createPolicyRuleRoleApproval = policyRuleRoleApprovalInsertSchema;
export type CreatePolicyRuleRoleApproval = z.infer<
typeof createPolicyRuleRoleApproval
>;

export const createPolicyRuleRoleApprovalRecord =
policyRuleRoleApprovalRecordInsertSchema;
export type CreatePolicyRuleRoleApprovalRecord = z.infer<
typeof createPolicyRuleRoleApprovalRecord
>;

// Export update schemas
export const updatePolicyRuleRoleApproval =
policyRuleRoleApprovalInsertSchema.partial();
export type UpdatePolicyRuleRoleApproval = z.infer<
typeof updatePolicyRuleRoleApproval
>;

export const updatePolicyRuleRoleApprovalRecord =
policyRuleRoleApprovalRecordInsertSchema.partial();
export type UpdatePolicyRuleRoleApprovalRecord = z.infer<
typeof updatePolicyRuleRoleApprovalRecord
>;

// Export model types
export type PolicyRuleRoleApproval = InferSelectModel<
typeof policyRuleRoleApproval
>;
export type PolicyRuleRoleApprovalRecord = InferSelectModel<
typeof policyRuleRoleApprovalRecord
>;
40 changes: 0 additions & 40 deletions packages/db/src/schema/rules/approval-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";

import { user } from "../auth.js";
import {
baseApprovalRecordFields,
baseApprovalRecordValidationFields,
} from "./approval-base.js";
import {
basePolicyRuleFields,
basePolicyRuleValidationFields,
Expand All @@ -23,19 +19,6 @@ export const policyRuleUserApproval = pgTable("policy_rule_user_approval", {
.references(() => user.id),
});

// Approval records specific to user approval rules
export const policyRuleUserApprovalRecord = pgTable(
"policy_rule_user_approval_record",
{
...baseApprovalRecordFields,

// Link to the user approval rule
ruleId: uuid("rule_id")
.notNull()
.references(() => policyRuleUserApproval.id, { onDelete: "cascade" }),
},
);

// Validation schemas
export const policyRuleUserApprovalInsertSchema = createInsertSchema(
policyRuleUserApproval,
Expand All @@ -45,43 +28,20 @@ export const policyRuleUserApprovalInsertSchema = createInsertSchema(
},
).omit({ id: true, createdAt: true });

export const policyRuleUserApprovalRecordInsertSchema = createInsertSchema(
policyRuleUserApprovalRecord,
{
...baseApprovalRecordValidationFields,
ruleId: z.string().uuid(),
},
).omit({ id: true, createdAt: true, updatedAt: true });

// Export create schemas
export const createPolicyRuleUserApproval = policyRuleUserApprovalInsertSchema;
export type CreatePolicyRuleUserApproval = z.infer<
typeof createPolicyRuleUserApproval
>;

export const createPolicyRuleUserApprovalRecord =
policyRuleUserApprovalRecordInsertSchema;
export type CreatePolicyRuleUserApprovalRecord = z.infer<
typeof createPolicyRuleUserApprovalRecord
>;

// Export update schemas
export const updatePolicyRuleUserApproval =
policyRuleUserApprovalInsertSchema.partial();
export type UpdatePolicyRuleUserApproval = z.infer<
typeof updatePolicyRuleUserApproval
>;

export const updatePolicyRuleUserApprovalRecord =
policyRuleUserApprovalRecordInsertSchema.partial();
export type UpdatePolicyRuleUserApprovalRecord = z.infer<
typeof updatePolicyRuleUserApprovalRecord
>;

// Export model types
export type PolicyRuleUserApproval = InferSelectModel<
typeof policyRuleUserApproval
>;
export type PolicyRuleUserApprovalRecord = InferSelectModel<
typeof policyRuleUserApprovalRecord
>;
2 changes: 1 addition & 1 deletion packages/db/src/schema/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ export * from "./base.js";

export * from "./deny-window.js";

export * from "./approval-base.js";
export * from "./approval-user.js";
export * from "./approval-team.js";
export * from "./approval-role.js";
export * from "./approval-any.js";
export * from "./approval-record.js";
export * from "./rule-relations.js";
export * from "./deployment-selector.js";
Loading
Loading