From c839b18d4e2218e9f791adca1d4410a0d3448731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Tue, 31 Mar 2026 14:00:26 +0200 Subject: [PATCH] refactor(api): extract system role config support --- .../system-role-config-support.test.ts | 34 +++++++++++++++++++ .../src/router/system-role-config-support.ts | 23 +++++++++++++ packages/api/src/router/system-role-config.ts | 23 ++++--------- 3 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 packages/api/src/__tests__/system-role-config-support.test.ts create mode 100644 packages/api/src/router/system-role-config-support.ts diff --git a/packages/api/src/__tests__/system-role-config-support.test.ts b/packages/api/src/__tests__/system-role-config-support.test.ts new file mode 100644 index 0000000..8179f5b --- /dev/null +++ b/packages/api/src/__tests__/system-role-config-support.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import { + buildSystemRoleConfigUpdateData, + systemRoleConfigUpdateInputSchema, +} from "../router/system-role-config-support.js"; + +describe("system role config support", () => { + it("validates the update input schema", () => { + expect(systemRoleConfigUpdateInputSchema.parse({ + role: "ADMIN", + label: "Administrators", + description: null, + color: "#000000", + defaultPermissions: ["users.read"], + })).toEqual({ + role: "ADMIN", + label: "Administrators", + description: null, + color: "#000000", + defaultPermissions: ["users.read"], + }); + }); + + it("builds sparse update payloads", () => { + expect(buildSystemRoleConfigUpdateData({ + role: "MANAGER", + description: null, + defaultPermissions: ["projects.write"], + })).toEqual({ + description: null, + defaultPermissions: ["projects.write"], + }); + }); +}); diff --git a/packages/api/src/router/system-role-config-support.ts b/packages/api/src/router/system-role-config-support.ts new file mode 100644 index 0000000..c6fbde2 --- /dev/null +++ b/packages/api/src/router/system-role-config-support.ts @@ -0,0 +1,23 @@ +import type { Prisma } from "@capakraken/db"; +import { z } from "zod"; + +export const systemRoleConfigUpdateInputSchema = z.object({ + role: z.string(), + label: z.string().min(1).optional(), + description: z.string().nullable().optional(), + color: z.string().nullable().optional(), + defaultPermissions: z.array(z.string()).optional(), +}); + +export type SystemRoleConfigUpdateInput = z.infer; + +export function buildSystemRoleConfigUpdateData( + input: SystemRoleConfigUpdateInput, +): Prisma.SystemRoleConfigUpdateInput { + return { + ...(input.label !== undefined ? { label: input.label } : {}), + ...(input.description !== undefined ? { description: input.description } : {}), + ...(input.color !== undefined ? { color: input.color } : {}), + ...(input.defaultPermissions !== undefined ? { defaultPermissions: input.defaultPermissions } : {}), + }; +} diff --git a/packages/api/src/router/system-role-config.ts b/packages/api/src/router/system-role-config.ts index 770f22d..6852f8e 100644 --- a/packages/api/src/router/system-role-config.ts +++ b/packages/api/src/router/system-role-config.ts @@ -1,6 +1,9 @@ -import { z } from "zod"; import { adminProcedure, createTRPCRouter, invalidateRoleDefaultsCache } from "../trpc.js"; import { createAuditEntry } from "../lib/audit.js"; +import { + buildSystemRoleConfigUpdateData, + systemRoleConfigUpdateInputSchema, +} from "./system-role-config-support.js"; export const systemRoleConfigRouter = createTRPCRouter({ /** List all role configs (sorted by sortOrder) */ @@ -12,29 +15,15 @@ export const systemRoleConfigRouter = createTRPCRouter({ /** Update a role's default permissions, label, description, and color */ update: adminProcedure - .input( - z.object({ - role: z.string(), - label: z.string().min(1).optional(), - description: z.string().nullable().optional(), - color: z.string().nullable().optional(), - defaultPermissions: z.array(z.string()).optional(), - }), - ) + .input(systemRoleConfigUpdateInputSchema) .mutation(async ({ ctx, input }) => { const existing = await ctx.db.systemRoleConfig.findUnique({ where: { role: input.role as never }, }); - const data: Record = {}; - if (input.label !== undefined) data.label = input.label; - if (input.description !== undefined) data.description = input.description; - if (input.color !== undefined) data.color = input.color; - if (input.defaultPermissions !== undefined) data.defaultPermissions = input.defaultPermissions; - const result = await ctx.db.systemRoleConfig.update({ where: { role: input.role as never }, - data, + data: buildSystemRoleConfigUpdateData(input), }); // Invalidate cached role defaults so changes take effect immediately