refactor(api): extract timeline shift mutation routing
This commit is contained in:
@@ -0,0 +1,59 @@
|
|||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
vi.mock("../sse/event-bus.js", () => ({
|
||||||
|
emitProjectShifted: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../router/timeline-project-load-support.js", () => ({
|
||||||
|
loadProjectShiftContext: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../router/timeline-shift-procedure-support.js", () => ({
|
||||||
|
applyTimelineProjectShift: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { emitProjectShifted } from "../sse/event-bus.js";
|
||||||
|
import { loadProjectShiftContext } from "../router/timeline-project-load-support.js";
|
||||||
|
import { applyTimelineProjectShiftMutation } from "../router/timeline-shift-router-support.js";
|
||||||
|
import { applyTimelineProjectShift } from "../router/timeline-shift-procedure-support.js";
|
||||||
|
|
||||||
|
const emitProjectShiftedMock = vi.mocked(emitProjectShifted);
|
||||||
|
const loadProjectShiftContextMock = vi.mocked(loadProjectShiftContext);
|
||||||
|
const applyTimelineProjectShiftMock = vi.mocked(applyTimelineProjectShift);
|
||||||
|
|
||||||
|
describe("timeline shift router support", () => {
|
||||||
|
it("loads context, applies the shift, emits the event, and returns the response payload", async () => {
|
||||||
|
const db = {} as never;
|
||||||
|
const context = { project: { id: "project_1" } } as never;
|
||||||
|
const result = {
|
||||||
|
project: { id: "project_1" },
|
||||||
|
validation: { valid: true },
|
||||||
|
event: { projectId: "project_1" },
|
||||||
|
} as never;
|
||||||
|
|
||||||
|
loadProjectShiftContextMock.mockResolvedValueOnce(context);
|
||||||
|
applyTimelineProjectShiftMock.mockResolvedValueOnce(result);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
applyTimelineProjectShiftMutation({
|
||||||
|
db,
|
||||||
|
projectId: "project_1",
|
||||||
|
newStartDate: new Date("2026-04-03T00:00:00.000Z"),
|
||||||
|
newEndDate: new Date("2026-04-12T00:00:00.000Z"),
|
||||||
|
}),
|
||||||
|
).resolves.toEqual({
|
||||||
|
project: { id: "project_1" },
|
||||||
|
validation: { valid: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(loadProjectShiftContextMock).toHaveBeenCalledWith(db, "project_1");
|
||||||
|
expect(applyTimelineProjectShiftMock).toHaveBeenCalledWith({
|
||||||
|
db,
|
||||||
|
projectId: "project_1",
|
||||||
|
newStartDate: new Date("2026-04-03T00:00:00.000Z"),
|
||||||
|
newEndDate: new Date("2026-04-12T00:00:00.000Z"),
|
||||||
|
context,
|
||||||
|
});
|
||||||
|
expect(emitProjectShiftedMock).toHaveBeenCalledWith({ projectId: "project_1" });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { PermissionKey, ShiftProjectSchema } from "@capakraken/shared";
|
||||||
|
import { managerProcedure, requirePermission } from "../trpc.js";
|
||||||
|
import { timelineAllocationMutationProcedures } from "./timeline-allocation-mutations.js";
|
||||||
|
import { applyTimelineProjectShiftMutation } from "./timeline-shift-router-support.js";
|
||||||
|
|
||||||
|
export const timelineMutationProcedures = {
|
||||||
|
...timelineAllocationMutationProcedures,
|
||||||
|
|
||||||
|
applyShift: managerProcedure
|
||||||
|
.input(ShiftProjectSchema)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
||||||
|
|
||||||
|
return applyTimelineProjectShiftMutation({
|
||||||
|
db: ctx.db,
|
||||||
|
projectId: input.projectId,
|
||||||
|
newStartDate: input.newStartDate,
|
||||||
|
newEndDate: input.newEndDate,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type { PrismaClient } from "@capakraken/db";
|
||||||
|
import { emitProjectShifted } from "../sse/event-bus.js";
|
||||||
|
import { loadProjectShiftContext } from "./timeline-project-load-support.js";
|
||||||
|
import { applyTimelineProjectShift } from "./timeline-shift-procedure-support.js";
|
||||||
|
|
||||||
|
export async function applyTimelineProjectShiftMutation(input: {
|
||||||
|
db: PrismaClient;
|
||||||
|
projectId: string;
|
||||||
|
newStartDate: Date;
|
||||||
|
newEndDate: Date;
|
||||||
|
}) {
|
||||||
|
const context = await loadProjectShiftContext(input.db, input.projectId);
|
||||||
|
const result = await applyTimelineProjectShift({
|
||||||
|
db: input.db,
|
||||||
|
projectId: input.projectId,
|
||||||
|
newStartDate: input.newStartDate,
|
||||||
|
newEndDate: input.newEndDate,
|
||||||
|
context,
|
||||||
|
});
|
||||||
|
|
||||||
|
emitProjectShifted(result.event);
|
||||||
|
|
||||||
|
return {
|
||||||
|
project: result.project,
|
||||||
|
validation: result.validation,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,38 +1,8 @@
|
|||||||
import { PermissionKey, ShiftProjectSchema } from "@capakraken/shared";
|
import { createTRPCRouter } from "../trpc.js";
|
||||||
import { emitProjectShifted } from "../sse/event-bus.js";
|
import { timelineMutationProcedures } from "./timeline-mutations.js";
|
||||||
import { createTRPCRouter, managerProcedure, requirePermission } from "../trpc.js";
|
|
||||||
import { timelineAllocationMutationProcedures } from "./timeline-allocation-mutations.js";
|
|
||||||
import { timelineReadProcedures } from "./timeline-read.js";
|
import { timelineReadProcedures } from "./timeline-read.js";
|
||||||
import { loadProjectShiftContext } from "./timeline-project-load-support.js";
|
|
||||||
import { applyTimelineProjectShift } from "./timeline-shift-procedure-support.js";
|
|
||||||
|
|
||||||
export const timelineRouter = createTRPCRouter({
|
export const timelineRouter = createTRPCRouter({
|
||||||
...timelineReadProcedures,
|
...timelineReadProcedures,
|
||||||
...timelineAllocationMutationProcedures,
|
...timelineMutationProcedures,
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply a project shift: validate, then commit all allocation date changes.
|
|
||||||
* Reads includeSaturday from each allocation's metadata.
|
|
||||||
*/
|
|
||||||
applyShift: managerProcedure
|
|
||||||
.input(ShiftProjectSchema)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
|
||||||
const { projectId, newStartDate, newEndDate } = input;
|
|
||||||
const context = await loadProjectShiftContext(ctx.db, projectId);
|
|
||||||
const result = await applyTimelineProjectShift({
|
|
||||||
db: ctx.db,
|
|
||||||
projectId,
|
|
||||||
newStartDate,
|
|
||||||
newEndDate,
|
|
||||||
context,
|
|
||||||
});
|
|
||||||
|
|
||||||
emitProjectShifted(result.event);
|
|
||||||
|
|
||||||
return {
|
|
||||||
project: result.project,
|
|
||||||
validation: result.validation,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user