import { ok, err } from "@tailor-platform/erp-kit/core"; import type { Transaction, Updateable } from "../generated/kysely-tailordb"; import { RoleNotFoundError, RoleNotActiveError, MissingRequiredFieldError, RoleAlreadyExistsError, InvalidPermissionError, } from "../lib/errors.generated"; import { validatePermissionKey } from "../lib/validatePermissionKey"; export type UpdateRoleInput = { id: string } & { name?: string; description?: string | null; permissions?: string[] | null; }; /** * Function: updateRole * * Updates an existing role's name or description. * Only ACTIVE roles can be updated. Existing UserRole and RolePermission associations remain intact. */ export async function run>( db: Transaction, input: UpdateRoleInput & Omit, "status">, ) { const { id, name, description, permissions, ...customFields } = input; const role = await db .selectFrom("Role") .selectAll() .where("id", "=", id) .forUpdate() .executeTakeFirst(); if (!role) { return err(new RoleNotFoundError(id)); } if (role.status !== "ACTIVE") { return err(new RoleNotActiveError(id)); } if (name?.trim() === "") { return err(new MissingRequiredFieldError("name")); } if (permissions) { for (const key of permissions) { if (!validatePermissionKey(key)) { return err(new InvalidPermissionError(key)); } } } if (name !== undefined && name !== role.name) { const existingRole = await db .selectFrom("Role") .selectAll() .where("name", "=", name) .forUpdate() .executeTakeFirst(); if (existingRole) { return err(new RoleAlreadyExistsError(name)); } } const updateBody: Updateable<"Role"> = { ...(customFields as Updateable<"Role">), }; if (name !== undefined) updateBody.name = name; if (description !== undefined) updateBody.description = description; if (permissions !== undefined) updateBody.permissions = permissions; if (Object.keys(updateBody).length === 0) { return ok({ role }); } const updatedRole = await db .updateTable("Role") .set(updateBody) .where("id", "=", id) .returningAll() .executeTakeFirstOrThrow(); return ok({ role: updatedRole }); }