metis-desk/modules/resources/src/api/application/resource-input.mapper.ts
David fe6ec361b7 feat(companies): vincular identidades locales a activos y añadir cuenta de acceso a FolderMapping
- Asociar entornos locales con activos y conservar su trazabilidad.
- Registrar la cuenta de acceso del mapeo con historial de cambios.
- Calcular cobertura documental específica sin alterar la cobertura general.
- Añadir migraciones, validaciones, endpoints protegidos, pruebas y documentación.
2026-10-21 11:25:23 +02:00

187 lines
6.0 KiB
TypeScript

import type { UniqueID } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import type { Resource } from "../domain/resource.aggregate";
import { ResourceCatalogError } from "../domain/resource-definition";
import type { ResourceInput } from "../domain/resource-values";
import { ResourceSpecification, ResourceValues } from "../domain/value-objects/resource-values.vo";
import { directoryDomainInput } from "./directory-domain-input";
import type { InventoryContext } from "./resource.repository";
import { mapSmbAccessInput } from "./smb-access-input";
export function mapResourceInput(
input: ResourceInput,
companyId: UniqueID,
context: InventoryContext,
previous?: Resource
) {
if (previous && previous.values.toPrimitive().templateId !== input.templateId.toLowerCase()) {
return Result.fail(new ResourceCatalogError("TEMPLATE_FIXED"));
}
const captured = previous
? Result.ok(previous.specification)
: captureResourceSpecification(input, companyId, context);
if (!captured.isSuccess) {
return captured;
}
const specification = captured.data;
const directory = directoryDomainInput(input, context.catalog);
if (!directory.isSuccess) {
return directory;
}
const normalized = directory.data
? {
...input,
characteristics: {
...directory.data.characteristics,
[directory.data.dnsId]: directory.data.identity.dnsName,
[directory.data.netbiosId]: directory.data.identity.netbiosName ?? "",
},
}
: input;
const mailboxInput = withMailboxIntegration(normalized, context);
const integrated = mapSmbAccessInput(mailboxInput, context, previous);
if (!integrated.isSuccess) {
return integrated;
}
const compatibleInput = integrated.data;
const templateKey = context.catalog.definitions
.find((item) => item.id.toPrimitive() === input.templateId.toLowerCase())
?.stockKey.getOrUndefined();
if (templateKey === "template:access" && compatibleInput.integration?.kind !== "access") {
return Result.fail(new ResourceCatalogError("REFERENCE_UNAVAILABLE"));
}
const references = validateReferences(compatibleInput, context);
if (!references.isSuccess) {
return references;
}
const values = ResourceValues.create(compatibleInput, specification);
return values.isSuccess ? Result.ok({ specification, values: values.data }) : values;
}
export function captureResourceSpecification(
input: Pick<ResourceInput, "templateId">,
companyId: UniqueID,
context: Pick<InventoryContext, "catalog">,
options: { existing?: boolean } = {}
) {
const template = context.catalog
.visible(Maybe.some(companyId))
.find((item) => item.id.toPrimitive() === input.templateId.toLowerCase());
if (
!template ||
template.values.data.kind !== "template" ||
!(options.existing || context.catalog.active(template, Maybe.some(companyId)))
) {
return Result.fail(new ResourceCatalogError("TEMPLATE_UNAVAILABLE"));
}
const fields = template.values.data.fields.map((binding) => {
const field = context.catalog.definitions.find(
(item) => item.id.toPrimitive() === binding.fieldId
);
if (!field || field.values.data.kind !== "field") {
throw new Error("Plantilla persistida inconsistente.");
}
return {
fieldId: field.id.toPrimitive(),
name: field.values.data.name,
type: field.values.data.type,
unit: field.values.data.unit,
choices: [...field.values.data.choices],
required: binding.required,
...(binding.sectionId ? { sectionId: binding.sectionId } : {}),
};
});
const created = ResourceSpecification.create({
templateId: template.id.toPrimitive(),
templateName: template.values.data.name,
...(template.values.data.sections ? { sections: [...template.values.data.sections] } : {}),
fields,
});
if (!created.isSuccess) {
return created;
}
return created;
}
function withMailboxIntegration(input: ResourceInput, context: InventoryContext): ResourceInput {
const compatibleInput = { ...input };
if (!compatibleInput.integration) {
const template = context.catalog.definitions.find(
(item) => item.id.toPrimitive() === input.templateId.toLowerCase()
);
if (template?.stockKey.getOrUndefined() === "template:mailbox") {
const addressFieldId = context.catalog.definitions
.find((item) => item.stockKey.getOrUndefined() === "field:email")
?.id.toPrimitive();
const contextFieldId = context.catalog.definitions
.find((item) => item.stockKey.getOrUndefined() === "field:context")
?.id.toPrimitive();
if (!(addressFieldId && contextFieldId)) {
throw new Error("Campos de buzón ausentes.");
}
compatibleInput.integration = {
kind: "mailbox",
addressFieldId,
contextFieldId,
mailboxKind: "corporate",
accountId: null,
};
}
}
return compatibleInput;
}
function validateReferences(compatibleInput: ResourceInput, context: InventoryContext) {
const integration = compatibleInput.integration;
if (integration) {
const expected =
integration.kind === "mailbox"
? ([[integration.accountId, "account"]] as const)
: ([
[integration.resourceTypeId, "resource_type"],
[integration.systemId, "system"],
[integration.environmentId, "environment"],
] as const);
for (const [id, kind] of expected) {
if (
id &&
!context.references.some((item) => item.id === id.toLowerCase() && item.kind === kind)
) {
return Result.fail(new ResourceCatalogError("REFERENCE_UNAVAILABLE"));
}
}
if (
integration.kind === "access" &&
integration.environmentId &&
!context.references.some(
(item) =>
item.id === integration.environmentId?.toLowerCase() &&
item.systemId === integration.systemId
)
) {
return Result.fail(new ResourceCatalogError("REFERENCE_UNAVAILABLE"));
}
}
return Result.ok(true);
}