From b1799e4f54e98b47eade805ea637db2f9343ced6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Tue, 31 Mar 2026 21:24:28 +0200 Subject: [PATCH] refactor(api): extract computation graph procedures --- docs/api-router-procedure-support-backlog.md | 1 + ...omputation-graph-procedure-support.test.ts | 52 +++++++++++++++++++ .../computation-graph-procedure-support.ts | 37 +++++++++++++ packages/api/src/router/computation-graph.ts | 37 +++++-------- 4 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 packages/api/src/__tests__/computation-graph-procedure-support.test.ts create mode 100644 packages/api/src/router/computation-graph-procedure-support.ts diff --git a/docs/api-router-procedure-support-backlog.md b/docs/api-router-procedure-support-backlog.md index 8b6ca85..b795230 100644 --- a/docs/api-router-procedure-support-backlog.md +++ b/docs/api-router-procedure-support-backlog.md @@ -27,6 +27,7 @@ Done - `calculation-rules` - `webhook` - `role` +- `computation-graph` Ready next - none in the conflict-safe backlog diff --git a/packages/api/src/__tests__/computation-graph-procedure-support.test.ts b/packages/api/src/__tests__/computation-graph-procedure-support.test.ts new file mode 100644 index 0000000..1acc599 --- /dev/null +++ b/packages/api/src/__tests__/computation-graph-procedure-support.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it, vi } from "vitest"; +import { + getProjectGraphData, + getResourceGraphData, +} from "../router/computation-graph-procedure-support.js"; + +const { readResourceGraphSnapshot, readProjectGraphSnapshot } = vi.hoisted(() => ({ + readResourceGraphSnapshot: vi.fn(), + readProjectGraphSnapshot: vi.fn(), +})); + +vi.mock("../router/computation-graph-resource.js", () => ({ + readResourceGraphSnapshot, +})); + +vi.mock("../router/computation-graph-project.js", () => ({ + readProjectGraphSnapshot, +})); + +describe("computation-graph-procedure-support", () => { + it("delegates resource graph reads to the snapshot reader", async () => { + const ctx = { db: {} } as never; + const snapshot = { nodes: [], links: [], meta: { resourceEid: "eid", resourceName: "Bruce" } }; + readResourceGraphSnapshot.mockResolvedValue(snapshot); + + const result = await getResourceGraphData(ctx, { + resourceId: "resource_1", + month: "2026-01", + }); + + expect(result).toBe(snapshot); + expect(readResourceGraphSnapshot).toHaveBeenCalledWith(ctx, { + resourceId: "resource_1", + month: "2026-01", + }); + }); + + it("delegates project graph reads to the snapshot reader", async () => { + const ctx = { db: {} } as never; + const snapshot = { nodes: [], links: [], meta: { projectCode: "GDM", projectName: "Gelddruckmaschine" } }; + readProjectGraphSnapshot.mockResolvedValue(snapshot); + + const result = await getProjectGraphData(ctx, { + projectId: "project_1", + }); + + expect(result).toBe(snapshot); + expect(readProjectGraphSnapshot).toHaveBeenCalledWith(ctx, { + projectId: "project_1", + }); + }); +}); diff --git a/packages/api/src/router/computation-graph-procedure-support.ts b/packages/api/src/router/computation-graph-procedure-support.ts new file mode 100644 index 0000000..1209055 --- /dev/null +++ b/packages/api/src/router/computation-graph-procedure-support.ts @@ -0,0 +1,37 @@ +import { z } from "zod"; +import type { TRPCContext } from "../trpc.js"; +import { createComputationGraphDetailProcedures } from "./computation-graph-detail.js"; +import { readProjectGraphSnapshot } from "./computation-graph-project.js"; +import { readResourceGraphSnapshot } from "./computation-graph-resource.js"; + +export const ResourceGraphInputSchema = z.object({ + resourceId: z.string(), + month: z.string().regex(/^\d{4}-\d{2}$/), +}); + +export const ProjectGraphInputSchema = z.object({ + projectId: z.string(), +}); + +type ComputationGraphProcedureContext = Pick; + +export const computationGraphDetailProcedures = createComputationGraphDetailProcedures({ + resourceGraphInputSchema: ResourceGraphInputSchema, + projectGraphInputSchema: ProjectGraphInputSchema, + readResourceGraphSnapshot, + readProjectGraphSnapshot, +}); + +export async function getResourceGraphData( + ctx: ComputationGraphProcedureContext, + input: z.infer, +) { + return readResourceGraphSnapshot(ctx, input); +} + +export async function getProjectGraphData( + ctx: ComputationGraphProcedureContext, + input: z.infer, +) { + return readProjectGraphSnapshot(ctx, input); +} diff --git a/packages/api/src/router/computation-graph.ts b/packages/api/src/router/computation-graph.ts index f1a141a..626f36e 100644 --- a/packages/api/src/router/computation-graph.ts +++ b/packages/api/src/router/computation-graph.ts @@ -1,27 +1,14 @@ -import { z } from "zod"; -import { createTRPCRouter, controllerProcedure, type TRPCContext } from "../trpc.js"; -import { createComputationGraphDetailProcedures } from "./computation-graph-detail.js"; -import { readProjectGraphSnapshot } from "./computation-graph-project.js"; -import { readResourceGraphSnapshot } from "./computation-graph-resource.js"; +import { createTRPCRouter, controllerProcedure } from "../trpc.js"; +import { + computationGraphDetailProcedures, + getProjectGraphData, + getResourceGraphData, + ProjectGraphInputSchema, + ResourceGraphInputSchema, +} from "./computation-graph-procedure-support.js"; import { type GraphLink, type GraphNode } from "./computation-graph-shared.js"; export type { GraphLink, GraphNode } from "./computation-graph-shared.js"; -const resourceGraphInputSchema = z.object({ - resourceId: z.string(), - month: z.string().regex(/^\d{4}-\d{2}$/), -}); - -const projectGraphInputSchema = z.object({ - projectId: z.string(), -}); - -const computationGraphDetailProcedures = createComputationGraphDetailProcedures({ - resourceGraphInputSchema, - projectGraphInputSchema, - readResourceGraphSnapshot, - readProjectGraphSnapshot, -}); - // ─── Router ───────────────────────────────────────────────────────────────── export const computationGraphRouter = createTRPCRouter({ @@ -31,13 +18,13 @@ export const computationGraphRouter = createTRPCRouter({ * for a single resource in a single month. */ getResourceData: controllerProcedure - .input(resourceGraphInputSchema) - .query(({ ctx, input }) => readResourceGraphSnapshot(ctx, input)), + .input(ResourceGraphInputSchema) + .query(({ ctx, input }) => getResourceGraphData(ctx, input)), /** * Project View: Estimate, Commercial, Experience, Effort, Spread, Budget */ getProjectData: controllerProcedure - .input(projectGraphInputSchema) - .query(({ ctx, input }) => readProjectGraphSnapshot(ctx, input)), + .input(ProjectGraphInputSchema) + .query(({ ctx, input }) => getProjectGraphData(ctx, input)), });