metis-desk/modules/assets/src/api/infrastructure/http/workstation.routes.ts
David 91f1d07e97 Add tests for current operating systems and workplace asset management
- Implement `current-operating-systems-ui.test.py` to validate the functionality of current operating systems in the UI, including tests for source and states, computer changes, responsive design, error handling, and version saving.
- Create `workplace-add-asset.test.py` to test the addition of assets to workstations, covering scenarios such as conflict resolution, responsive design on mobile, error handling during asset creation, and HTTP guards for asset management.
2026-10-25 13:46:06 +01:00

126 lines
3.8 KiB
TypeScript

import type { Application, RequestHandler, Response } from "express";
import { Router } from "express";
import { z } from "zod/v4";
import { AssetParamsSchema, InventoryParamsSchema } from "../../../common/inventory";
import { SaveWorkstationRequestSchema } from "../../../common/workstations";
import type { WorkstationUseCases } from "../../application/workstation.use-cases";
import { buildWorkstationResponse } from "../../application/workstation-response.builder";
function fail(response: Response, code: string) {
response
.status(code === "INVALID_REQUEST" ? 400 : code.endsWith("NOT_FOUND") ? 404 : 409)
.json({ code, requestId: response.getHeader("x-request-id") });
}
export function registerWorkstationRoutes(
app: Application,
useCases: WorkstationUseCases,
authenticate: RequestHandler
) {
const router = Router();
for (const path of ["/:companyId/workstations", "/:companyId/workstations/:id/update"]) {
router.post(path, (request, response, next) => {
const params = (
path.endsWith("update") ? AssetParamsSchema : InventoryParamsSchema
).safeParse(request.params);
const body = SaveWorkstationRequestSchema.safeParse(request.body);
const revision = z
.string()
.regex(/^"[0-9a-f-]{36}"$/i)
.optional()
.safeParse(request.get("if-match"));
const actor = z
.object({ userId: z.uuid() })
.safeParse("session" in request ? request.session : undefined);
if (
!(
params.success &&
body.success &&
revision.success &&
actor.success &&
z.strictObject({}).safeParse(request.query).success
)
) {
fail(response, "INVALID_REQUEST");
return;
}
const id =
"id" in params.data && typeof params.data.id === "string" ? params.data.id : undefined;
useCases
.save(
params.data.companyId,
id,
body.data.values,
actor.data.userId,
revision.data?.slice(1, -1)
)
.then((result) => {
if (!result.isSuccess) {
fail(response, result.error.code);
return;
}
response.status(id ? 200 : 201).json({
workstation: buildWorkstationResponse(result.data.workstation, result.data.actorNames),
});
})
.catch(next);
});
}
for (const action of ["decommission", "delete"] as const) {
router.post(`/:companyId/workstations/:id/${action}`, (request, response, next) => {
const params = AssetParamsSchema.safeParse(request.params);
const actor = z
.object({ userId: z.uuid() })
.safeParse("session" in request ? request.session : undefined);
if (
!(
params.success &&
actor.success &&
z.strictObject({}).safeParse(request.body).success &&
z.strictObject({}).safeParse(request.query).success
)
) {
fail(response, "INVALID_REQUEST");
return;
}
if (action === "delete") {
useCases
.remove(params.data.companyId, params.data.id)
.then((result) => {
if (!result.isSuccess) {
fail(response, result.error.code);
return;
}
response.json({ deleted: true });
})
.catch(next);
return;
}
useCases
.decommission(params.data.companyId, params.data.id, actor.data.userId)
.then((result) => {
if (!result.isSuccess) {
fail(response, result.error.code);
return;
}
response.json({
workstation: buildWorkstationResponse(result.data.workstation, result.data.actorNames),
});
})
.catch(next);
});
}
app.use("/api/assets", authenticate, router);
}