91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
|
|
import { createHash } from "node:crypto";
|
||
|
|
|
||
|
|
import { UniqueID } from "@repo/rdx-ddd";
|
||
|
|
import { Maybe } from "@repo/rdx-utils";
|
||
|
|
import { z } from "zod/v4";
|
||
|
|
|
||
|
|
import { ProcessTemplate } from "../../domain/process-template.aggregate";
|
||
|
|
import { ProcessTemplateRevision } from "../../domain/process-template-revision.entity";
|
||
|
|
import { ProcessTemplateInputSchema } from "../../domain/process-values";
|
||
|
|
import { ProcessTemplateValues } from "../../domain/value-objects/process-template-values";
|
||
|
|
|
||
|
|
import { persistedJson } from "./work-profile-persistence.mapper";
|
||
|
|
export type ProcessTemplateRow = { id: string; nameKey: string; values: unknown };
|
||
|
|
export type ProcessTemplateRevisionRow = {
|
||
|
|
id: string;
|
||
|
|
itemId: string;
|
||
|
|
position: number;
|
||
|
|
actorId: string | null;
|
||
|
|
recordedAt: string;
|
||
|
|
values: unknown;
|
||
|
|
};
|
||
|
|
function identity(value: string) {
|
||
|
|
const parsed = UniqueID.create(value);
|
||
|
|
if (!parsed.isSuccess) {
|
||
|
|
throw new Error("Identidad de proceso inconsistente.");
|
||
|
|
}
|
||
|
|
return parsed.data;
|
||
|
|
}
|
||
|
|
function mapped(raw: unknown) {
|
||
|
|
const values = ProcessTemplateValues.create(ProcessTemplateInputSchema.parse(persistedJson(raw)));
|
||
|
|
if (!values.isSuccess) {
|
||
|
|
throw new Error("Valores de proceso inconsistentes.");
|
||
|
|
}
|
||
|
|
return values.data;
|
||
|
|
}
|
||
|
|
export class ProcessTemplatePersistenceMapper {
|
||
|
|
toDomain(row: ProcessTemplateRow, revisions: readonly ProcessTemplateRevisionRow[]) {
|
||
|
|
const values = mapped(row.values);
|
||
|
|
const ordered = [...revisions].sort((a, b) => a.position - b.position);
|
||
|
|
const history = ordered.map((r, index) => {
|
||
|
|
if (
|
||
|
|
r.itemId !== row.id ||
|
||
|
|
r.position !== index ||
|
||
|
|
!z.iso.datetime().safeParse(r.recordedAt).success ||
|
||
|
|
r.recordedAt < (ordered[index - 1]?.recordedAt ?? "") ||
|
||
|
|
(!r.actorId && index !== 0)
|
||
|
|
) {
|
||
|
|
throw new Error("Historial de proceso inconsistente.");
|
||
|
|
}
|
||
|
|
return ProcessTemplateRevision.rehydrate(
|
||
|
|
{
|
||
|
|
values: mapped(r.values),
|
||
|
|
actorId: r.actorId ? Maybe.some(identity(r.actorId)) : Maybe.none(),
|
||
|
|
recordedAt: r.recordedAt,
|
||
|
|
},
|
||
|
|
identity(r.id)
|
||
|
|
);
|
||
|
|
});
|
||
|
|
if (
|
||
|
|
!(history.length && history.at(-1)?.values.equals(values)) ||
|
||
|
|
row.nameKey !== processTemplateNameKey(values.name.toPrimitive())
|
||
|
|
) {
|
||
|
|
throw new Error("Raíz de proceso inconsistente.");
|
||
|
|
}
|
||
|
|
|
||
|
|
return ProcessTemplate.rehydrate({ values, history }, identity(row.id));
|
||
|
|
}
|
||
|
|
toPersistence(item: ProcessTemplate) {
|
||
|
|
const values = item.values.toPrimitive();
|
||
|
|
return {
|
||
|
|
root: {
|
||
|
|
id: item.id.toPrimitive(),
|
||
|
|
nameKey: processTemplateNameKey(values.name),
|
||
|
|
values: JSON.stringify(values),
|
||
|
|
},
|
||
|
|
revisions: item.history.map((r, position) => ({
|
||
|
|
id: r.id.toPrimitive(),
|
||
|
|
itemId: item.id.toPrimitive(),
|
||
|
|
position,
|
||
|
|
actorId: r.actorId.getOrUndefined()?.toPrimitive() ?? null,
|
||
|
|
recordedAt: r.recordedAt,
|
||
|
|
values: JSON.stringify(r.values.toPrimitive()),
|
||
|
|
})),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function processTemplateNameKey(name: string) {
|
||
|
|
return createHash("sha256").update(name.toLowerCase()).digest("hex");
|
||
|
|
}
|