2026-10-05 15:01:32 +00:00
|
|
|
import { UniqueID } from "@repo/rdx-ddd";
|
|
|
|
|
import type { Sequelize, Transaction } from "sequelize";
|
|
|
|
|
import { QueryTypes } from "sequelize";
|
|
|
|
|
|
|
|
|
|
import type { Resource } from "../../domain/resource.aggregate";
|
|
|
|
|
import type { ResourceInput } from "../../domain/resource-values";
|
|
|
|
|
|
|
|
|
|
/** Proyecciones para los contratos de accesos y perfiles: la ficha editable pertenece a resources. */
|
|
|
|
|
export async function projectResource(
|
|
|
|
|
database: Sequelize,
|
|
|
|
|
resource: Resource,
|
|
|
|
|
transaction: Transaction
|
|
|
|
|
) {
|
|
|
|
|
const current = resource.values.toPrimitive();
|
|
|
|
|
const integration = current.integration;
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
if (!integration) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const companyId = resource.companyId.toPrimitive();
|
|
|
|
|
const id = resource.id.toPrimitive();
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
if (integration.kind === "mailbox") {
|
|
|
|
|
const values = mailboxProjection(current);
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
await database.query(
|
|
|
|
|
"INSERT INTO mailboxes (id,companyId,accountId,addressKey,`values`) VALUES (:id,:companyId,:accountId,:addressKey,:values) ON DUPLICATE KEY UPDATE accountId=VALUES(accountId),addressKey=VALUES(addressKey),`values`=VALUES(`values`)",
|
|
|
|
|
{
|
|
|
|
|
replacements: {
|
|
|
|
|
id,
|
|
|
|
|
companyId,
|
|
|
|
|
accountId: values.accountId,
|
|
|
|
|
addressKey: `${companyId}:${values.address}`,
|
|
|
|
|
values: JSON.stringify(values),
|
|
|
|
|
},
|
|
|
|
|
transaction,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
for (const [position, revision] of resource.history.entries()) {
|
|
|
|
|
await database.query(
|
|
|
|
|
"INSERT INTO mailbox_revisions (id,itemId,position,actorId,recordedAt,`values`) VALUES (:id,:itemId,:position,:actorId,:recordedAt,:values) ON DUPLICATE KEY UPDATE id=id",
|
|
|
|
|
{
|
|
|
|
|
replacements: {
|
|
|
|
|
id: revision.id.toPrimitive(),
|
|
|
|
|
itemId: id,
|
|
|
|
|
position,
|
|
|
|
|
actorId: revision.actorId.toPrimitive(),
|
|
|
|
|
recordedAt: revision.recordedAt,
|
|
|
|
|
values: JSON.stringify(mailboxProjection(revision.values.toPrimitive())),
|
|
|
|
|
},
|
|
|
|
|
transaction,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const roots = await database.query<{ id: string }>(
|
|
|
|
|
"SELECT id FROM access_workspaces WHERE companyId=:companyId",
|
|
|
|
|
{ replacements: { companyId }, transaction, type: QueryTypes.SELECT }
|
|
|
|
|
);
|
|
|
|
|
let workspaceId = roots[0]?.id;
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
if (!workspaceId) {
|
|
|
|
|
workspaceId = UniqueID.generateNewID().toPrimitive();
|
|
|
|
|
await database.query(
|
|
|
|
|
"INSERT INTO access_workspaces (id,companyId,scopeKey) VALUES (:id,:companyId,:companyId)",
|
|
|
|
|
{ replacements: { id: workspaceId, companyId }, transaction }
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
const values = accessProjection(current);
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
await database.query(
|
|
|
|
|
"INSERT INTO access_items (id,workspaceId,kind,`values`) VALUES (:id,:workspaceId,'resource',:values) ON DUPLICATE KEY UPDATE `values`=VALUES(`values`)",
|
|
|
|
|
{ replacements: { id, workspaceId, values: JSON.stringify(values) }, transaction }
|
|
|
|
|
);
|
|
|
|
|
for (const [sequence, revision] of resource.history.entries()) {
|
|
|
|
|
await database.query(
|
|
|
|
|
"INSERT INTO access_revisions (id,itemId,sequence,actorId,recordedAt,`values`) VALUES (:id,:itemId,:sequence,:actorId,:recordedAt,:values) ON DUPLICATE KEY UPDATE id=id",
|
|
|
|
|
{
|
|
|
|
|
replacements: {
|
|
|
|
|
id: revision.id.toPrimitive(),
|
|
|
|
|
itemId: id,
|
|
|
|
|
sequence,
|
|
|
|
|
actorId: revision.actorId.toPrimitive(),
|
|
|
|
|
recordedAt: revision.recordedAt,
|
|
|
|
|
values: JSON.stringify(accessProjection(revision.values.toPrimitive())),
|
|
|
|
|
},
|
|
|
|
|
transaction,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
// El contexto es fijo; estas referencias preservan las FK utilizadas por permisos y necesidades.
|
|
|
|
|
for (const targetId of [
|
|
|
|
|
integration.resourceTypeId,
|
|
|
|
|
integration.systemId,
|
|
|
|
|
integration.environmentId,
|
|
|
|
|
].filter(Boolean)) {
|
|
|
|
|
const existing = await database.query<{ itemId: string }>(
|
|
|
|
|
"SELECT itemId FROM access_references WHERE itemId=:id AND targetId=:targetId",
|
|
|
|
|
{ replacements: { id, targetId }, transaction, type: QueryTypes.SELECT }
|
|
|
|
|
);
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
if (!existing.length) {
|
|
|
|
|
await database.query(
|
|
|
|
|
"INSERT INTO access_references (itemId,targetId) VALUES (:id,:targetId)",
|
|
|
|
|
{ replacements: { id, targetId }, transaction }
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
function mailboxProjection(input: ResourceInput) {
|
|
|
|
|
const integration = input.integration;
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
if (integration?.kind !== "mailbox") {
|
|
|
|
|
throw new Error("Recurso sin contexto de buzón.");
|
|
|
|
|
}
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
return {
|
|
|
|
|
address: input.characteristics[integration.addressFieldId],
|
|
|
|
|
kind: integration.mailboxKind,
|
|
|
|
|
context: input.characteristics[integration.contextFieldId] || null,
|
|
|
|
|
accountId: integration.accountId,
|
|
|
|
|
active: input.active,
|
|
|
|
|
notes: input.notes || null,
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
function accessProjection(input: ResourceInput) {
|
|
|
|
|
const integration = input.integration;
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
if (integration?.kind !== "access") {
|
|
|
|
|
throw new Error("Recurso sin contexto de acceso.");
|
|
|
|
|
}
|
2026-10-10 10:01:58 +00:00
|
|
|
|
2026-10-05 15:01:32 +00:00
|
|
|
return {
|
|
|
|
|
kind: "resource",
|
|
|
|
|
name: input.name,
|
|
|
|
|
active: input.active,
|
|
|
|
|
resourceTypeId: integration.resourceTypeId,
|
|
|
|
|
systemId: integration.systemId,
|
|
|
|
|
environmentId: integration.environmentId,
|
|
|
|
|
reference: input.characteristics[integration.referenceFieldId] || null,
|
|
|
|
|
notes: input.notes || null,
|
|
|
|
|
};
|
|
|
|
|
}
|