135 lines
4.2 KiB
TypeScript
135 lines
4.2 KiB
TypeScript
import { z } from "zod/v4";
|
|
|
|
import { ApplicationConfiguration } from "../../domain/application-configuration.aggregate";
|
|
import { ApplicationConfigurationRevision } from "../../domain/application-configuration-revision.entity";
|
|
import {
|
|
APPLICATION_STATE_TRANSITIONS,
|
|
ApplicationConfigurationInputSchema,
|
|
} from "../../domain/application-configuration-values";
|
|
import { ApplicationConfigurationValues } from "../../domain/value-objects/application-configuration-values.vo";
|
|
|
|
import { applicationSnapshot } from "./configuration-snapshots";
|
|
import { snapshotValues } from "./scalar-snapshot-normalization";
|
|
import { persistedJson, profileIdentity } from "./work-profile-persistence.mapper";
|
|
|
|
export type ApplicationRow = {
|
|
id: string;
|
|
companyId: string;
|
|
profileId: string;
|
|
resourceId: string;
|
|
values?: unknown;
|
|
currentKey: string | null;
|
|
};
|
|
|
|
export type ApplicationRevisionRow = {
|
|
id: string;
|
|
configurationId: string;
|
|
position: number;
|
|
values?: unknown;
|
|
actorId: string;
|
|
recordedAt: string;
|
|
};
|
|
|
|
function mapped(raw: unknown) {
|
|
const parsed = ApplicationConfigurationValues.create(
|
|
ApplicationConfigurationInputSchema.parse(persistedJson(raw))
|
|
);
|
|
|
|
if (!parsed.isSuccess) {
|
|
throw new Error("Valores de aplicación persistidos inconsistentes.");
|
|
}
|
|
|
|
return parsed.data;
|
|
}
|
|
|
|
export class ApplicationConfigurationPersistenceMapper {
|
|
toDomain(row: ApplicationRow, revisions: readonly ApplicationRevisionRow[]) {
|
|
const values = mapped(snapshotValues(applicationSnapshot, row));
|
|
const ordered = [...revisions].sort((a, b) => a.position - b.position);
|
|
const history = ordered.map((revision, index) => {
|
|
const value = mapped(snapshotValues(applicationSnapshot, revision));
|
|
const previous = ordered[index - 1];
|
|
|
|
if (
|
|
revision.configurationId !== row.id ||
|
|
revision.position !== index ||
|
|
!z.iso.datetime().safeParse(revision.recordedAt).success ||
|
|
revision.recordedAt < (previous?.recordedAt ?? "") ||
|
|
!values.sameTarget(value)
|
|
) {
|
|
throw new Error("Historial de aplicación persistido inconsistente.");
|
|
}
|
|
|
|
return ApplicationConfigurationRevision.rehydrate(
|
|
{
|
|
values: value,
|
|
actorId: profileIdentity(revision.actorId),
|
|
recordedAt: revision.recordedAt,
|
|
},
|
|
profileIdentity(revision.id)
|
|
);
|
|
});
|
|
|
|
if (!(history.length && ["pending", "configured"].includes(history[0]?.values.state ?? ""))) {
|
|
throw new Error("Estado inicial de aplicación persistido inconsistente.");
|
|
}
|
|
for (const [index, revision] of history.entries()) {
|
|
const previous = history[index - 1]?.values;
|
|
|
|
if (
|
|
previous &&
|
|
(!APPLICATION_STATE_TRANSITIONS[previous.state].some(
|
|
(state) => state === revision.values.state
|
|
) ||
|
|
(previous.closed && !previous.equals(revision.values)))
|
|
) {
|
|
throw new Error("Estados de aplicación históricos inconsistentes.");
|
|
}
|
|
}
|
|
const keys = applicationKeys(values);
|
|
|
|
if (
|
|
!history.at(-1)?.values.equals(values) ||
|
|
row.profileId !== values.profileId.toPrimitive() ||
|
|
row.resourceId !== values.resourceId.toPrimitive() ||
|
|
row.currentKey !== keys.currentKey
|
|
) {
|
|
throw new Error("Referencias de aplicación persistidas inconsistentes.");
|
|
}
|
|
|
|
return ApplicationConfiguration.rehydrate(
|
|
{ companyId: profileIdentity(row.companyId), values, history },
|
|
profileIdentity(row.id)
|
|
);
|
|
}
|
|
|
|
toPersistence(item: ApplicationConfiguration) {
|
|
const values = item.values.toPrimitive();
|
|
|
|
return {
|
|
root: {
|
|
id: item.id.toPrimitive(),
|
|
companyId: item.companyId.toPrimitive(),
|
|
...values,
|
|
...applicationKeys(item.values),
|
|
},
|
|
revisions: item.history.map((revision, position) => ({
|
|
id: revision.id.toPrimitive(),
|
|
configurationId: item.id.toPrimitive(),
|
|
position,
|
|
actorId: revision.actorId.toPrimitive(),
|
|
recordedAt: revision.recordedAt,
|
|
...revision.values.toPrimitive(),
|
|
})),
|
|
};
|
|
}
|
|
}
|
|
|
|
function applicationKeys(values: ApplicationConfigurationValues) {
|
|
return {
|
|
currentKey: values.closed
|
|
? null
|
|
: `${values.profileId.toPrimitive()}:${values.resourceId.toPrimitive()}`,
|
|
};
|
|
}
|