metis-desk/modules/assets/src/api/infrastructure/persistence/prepare-asset-template-updates.ts
David ded45e1c42 feat: add AssetRelatedInformation component for displaying asset profiles and software installations
test: implement resource template propagation tests for resource revisions

feat: create prepareResourceTemplateUpdates function for handling resource template updates

feat: add resource revision specifications for managing resource revisions in the database

test: add tests for entity column visibility and local storage handling

feat: implement entity column visibility storage for managing column preferences

feat: create synchronizeTemplateStructure utility for managing template field and section synchronization
2026-10-19 11:45:09 +02:00

115 lines
3.8 KiB
TypeScript

import type { UniqueID } from "@repo/rdx-ddd";
import { Result, synchronizeTemplateStructure } from "@repo/rdx-utils";
import type { Sequelize, Transaction } from "sequelize";
import { QueryTypes } from "sequelize";
import { captureAssetSpecification } from "../../application/asset-input.mapper";
import type { Asset } from "../../domain/asset.aggregate";
import type { AssetCatalog } from "../../domain/asset-catalog.aggregate";
import { AssetSpecification } from "../../domain/value-objects/asset-values.vo";
import { readInventory } from "./asset-inventory-relational";
import { AssetPersistenceMapper } from "./asset-persistence.mapper";
/** Prepara todas las compañías antes de escribir: un fallo de dominio no deja el catálogo a medio propagar. */
export async function prepareAssetTemplateUpdates(
database: Sequelize,
previous: AssetCatalog,
next: AssetCatalog,
actorId: UniqueID | undefined,
transaction: Transaction
) {
const updates: { previous: Asset; next: Asset }[] = [];
if (!actorId) {
return Result.ok(updates);
}
const templateIds = next.definitions
.filter((definition) => {
if (definition.values.data.kind !== "template") {
return false;
}
const old = previous.definitions.find((item) => item.id.equals(definition.id));
return (
old &&
(!old.values.equals(definition.values) ||
definition.values.data.fields.some((binding) => {
const field = next.definitions.find(
(item) => item.id.toPrimitive() === binding.fieldId
);
const oldField = previous.definitions.find(
(item) => item.id.toPrimitive() === binding.fieldId
);
return field && oldField && !field.values.equals(oldField.values);
}))
);
})
.map((item) => item.id.toPrimitive());
if (!templateIds.length) {
return Result.ok(updates);
}
const companies = await database.query<{ companyId: string }>(
"SELECT DISTINCT companyId FROM assets WHERE templateId IN (:templateIds) ORDER BY companyId",
{ replacements: { templateIds }, type: QueryTypes.SELECT, transaction }
);
const now = new Date().toISOString();
const mapper = new AssetPersistenceMapper();
for (const company of companies) {
await database.query("SELECT id FROM companies WHERE id=:id FOR UPDATE", {
replacements: { id: company.companyId },
type: QueryTypes.SELECT,
transaction,
});
const loaded = await readInventory(database, company.companyId, transaction);
for (const row of loaded.rows.filter((item) => templateIds.includes(item.templateId))) {
const item = mapper.toDomain(
row,
loaded.revisions.filter((revision) => revision.assetId === row.id)
);
const changed = synchronizeAssetTemplate(item, next, actorId, now);
if (!changed.isSuccess) {
return Result.fail(changed.error);
}
if (changed.data !== item) {
updates.push({ previous: item, next: changed.data });
}
}
}
return Result.ok(updates);
}
function synchronizeAssetTemplate(item: Asset, next: AssetCatalog, actorId: UniqueID, now: string) {
const captured = captureAssetSpecification(
{ templateId: item.values.toPrimitive().templateId },
item.companyId,
{ catalog: next },
{ existing: true }
);
if (!captured.isSuccess) {
return Result.fail(captured.error);
}
const structure = synchronizeTemplateStructure({
previous: item.specification.toPrimitive(),
next: captured.data.toPrimitive(),
values: item.values.toPrimitive().characteristics,
});
const specification = AssetSpecification.create({
...captured.data.toPrimitive(),
...structure,
});
if (!specification.isSuccess) {
return Result.fail(specification.error);
}
return item.synchronizeSpecification(specification.data, actorId, now);
}