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
115 lines
3.8 KiB
TypeScript
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);
|
|
}
|