- Implemented ProcessFeedback component for error handling in process forms. - Created ProcessHelp component to provide contextual assistance for users. - Added internationalization support for process-related strings in Spanish and English. - Developed ProcessStartForm for initiating new processes with person and template selection. - Introduced ProcessTemplateForm for creating and editing process templates. - Built ProcessTemplatesPage to display and manage process templates. - Enhanced ProcessesPage to list current processes and allow for creation and detail viewing. - Added unit tests for various components to ensure functionality and correctness.
184 lines
6.2 KiB
TypeScript
184 lines
6.2 KiB
TypeScript
import { type Application, type RequestHandler, type Response, Router } from "express";
|
|
import { z } from "zod/v4";
|
|
|
|
import {
|
|
ProcessTemplateInputSchema,
|
|
StartProcessInputSchema,
|
|
UpdateProcessInputSchema,
|
|
} from "../../../common/processes";
|
|
import type { ProcessUseCases } from "../../application/process.use-cases";
|
|
import { buildProcess, buildProcessTemplate } from "../../application/process-response.builder";
|
|
|
|
const companyParams = z.strictObject({ companyId: z.uuid().transform((v) => v.toLowerCase()) });
|
|
const itemParams = companyParams.extend({ id: z.uuid().transform((v) => v.toLowerCase()) });
|
|
const templateParams = z.strictObject({ id: z.uuid().transform((v) => v.toLowerCase()) });
|
|
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") });
|
|
}
|
|
function actorOf(request: unknown) {
|
|
return z.object({ session: z.object({ userId: z.uuid() }) }).safeParse(request);
|
|
}
|
|
export function registerProcessRoutes(
|
|
app: Application,
|
|
useCases: ProcessUseCases,
|
|
authenticate: RequestHandler
|
|
) {
|
|
const router = Router();
|
|
router.use((request, response, next) => {
|
|
if (!z.strictObject({}).safeParse(request.query).success) {
|
|
fail(response, "INVALID_REQUEST");
|
|
return;
|
|
}
|
|
next();
|
|
});
|
|
router.get("/templates", (_request, response, next) => {
|
|
useCases
|
|
.catalog()
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
return;
|
|
}
|
|
response.json({
|
|
templates: result.data.templates.map((t) => buildProcessTemplate(t, result.data)),
|
|
});
|
|
})
|
|
.catch(next);
|
|
});
|
|
for (const path of ["/templates", "/templates/:id/update"]) {
|
|
router.post(path, (request, response, next) => {
|
|
const body = z.strictObject({ values: ProcessTemplateInputSchema }).safeParse(request.body);
|
|
const params = path.endsWith("update")
|
|
? templateParams.safeParse(request.params)
|
|
: z.strictObject({}).safeParse(request.params);
|
|
const actor = actorOf(request);
|
|
if (!(body.success && params.success && actor.success)) {
|
|
fail(response, "INVALID_REQUEST");
|
|
return;
|
|
}
|
|
const id =
|
|
"id" in params.data && typeof params.data.id === "string" ? params.data.id : undefined;
|
|
useCases
|
|
.saveTemplate(body.data.values, actor.data.session.userId, id)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
return;
|
|
}
|
|
response
|
|
.status(id ? 200 : 201)
|
|
.json({ template: buildProcessTemplate(result.data.template, result.data.context) });
|
|
})
|
|
.catch(next);
|
|
});
|
|
}
|
|
router.get("/:companyId", (request, response, next) => {
|
|
const params = companyParams.safeParse(request.params);
|
|
if (!params.success) {
|
|
fail(response, "INVALID_REQUEST");
|
|
return;
|
|
}
|
|
useCases
|
|
.get(params.data.companyId)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
return;
|
|
}
|
|
response.json({
|
|
processes: result.data.processes.map((p) => buildProcess(p, result.data)),
|
|
});
|
|
})
|
|
.catch(next);
|
|
});
|
|
router.get("/:companyId/people/:id/review", (request, response, next) => {
|
|
const params = itemParams.safeParse(request.params);
|
|
if (!params.success) {
|
|
fail(response, "INVALID_REQUEST");
|
|
return;
|
|
}
|
|
useCases
|
|
.get(params.data.companyId)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
return;
|
|
}
|
|
const review = result.data.reviews.get(params.data.id);
|
|
if (!review) {
|
|
fail(response, "PROCESS_PERSON_NOT_FOUND");
|
|
return;
|
|
}
|
|
response.json({ review });
|
|
})
|
|
.catch(next);
|
|
});
|
|
router.post("/:companyId", (request, response, next) => {
|
|
const params = companyParams.safeParse(request.params);
|
|
const body = z.strictObject({ values: StartProcessInputSchema }).safeParse(request.body);
|
|
const actor = actorOf(request);
|
|
if (!(params.success && body.success && actor.success)) {
|
|
fail(response, "INVALID_REQUEST");
|
|
return;
|
|
}
|
|
useCases
|
|
.start(params.data.companyId, body.data.values, actor.data.session.userId)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
return;
|
|
}
|
|
response
|
|
.status(201)
|
|
.json({ process: buildProcess(result.data.process, result.data.context) });
|
|
})
|
|
.catch(next);
|
|
});
|
|
router.post("/:companyId/:id/update", (request, response, next) => {
|
|
const params = itemParams.safeParse(request.params);
|
|
const body = z.strictObject({ values: UpdateProcessInputSchema }).safeParse(request.body);
|
|
const actor = actorOf(request);
|
|
if (!(params.success && body.success && actor.success)) {
|
|
fail(response, "INVALID_REQUEST");
|
|
return;
|
|
}
|
|
useCases
|
|
.update(params.data.companyId, params.data.id, body.data.values, actor.data.session.userId)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
return;
|
|
}
|
|
response.json({ process: buildProcess(result.data.process, result.data.context) });
|
|
})
|
|
.catch(next);
|
|
});
|
|
router.post("/:companyId/:id/refresh", (request, response, next) => {
|
|
const params = itemParams.safeParse(request.params);
|
|
const body = z.strictObject({ revisionId: z.uuid() }).safeParse(request.body);
|
|
const actor = actorOf(request);
|
|
if (!(params.success && body.success && actor.success)) {
|
|
fail(response, "INVALID_REQUEST");
|
|
return;
|
|
}
|
|
useCases
|
|
.refresh(
|
|
params.data.companyId,
|
|
params.data.id,
|
|
body.data.revisionId,
|
|
actor.data.session.userId
|
|
)
|
|
.then((result) => {
|
|
if (!result.isSuccess) {
|
|
fail(response, result.error.code);
|
|
return;
|
|
}
|
|
response.json({ process: buildProcess(result.data.process, result.data.context) });
|
|
})
|
|
.catch(next);
|
|
});
|
|
app.use("/api/processes", authenticate, router);
|
|
}
|