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; if (!integration) { return; } const companyId = resource.companyId.toPrimitive(); const id = resource.id.toPrimitive(); if (integration.kind === "mailbox") { const values = mailboxProjection(current); 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, } ); } 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; 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); 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 } ); if (!existing.length) { await database.query( "INSERT INTO access_references (itemId,targetId) VALUES (:id,:targetId)", { replacements: { id, targetId }, transaction } ); } } } function mailboxProjection(input: ResourceInput) { const integration = input.integration; if (integration?.kind !== "mailbox") { throw new Error("Recurso sin contexto de buzón."); } 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, }; } function accessProjection(input: ResourceInput) { const integration = input.integration; if (integration?.kind !== "access") { throw new Error("Recurso sin contexto de acceso."); } 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, }; }