import type { IResourceInventoryService } from "@metis/resources/services"; import type { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import type { Sequelize, Transaction } from "sequelize"; import { QueryTypes } from "sequelize"; import type { IRemoteConnectionRepository, RemoteContext, } from "../../application/remote-connection.repository"; import { remoteReferences } from "../../application/remote-connection-policy"; import type { IWorkProfileInventoryService } from "../../application/work-profile.repository"; import { RemoteConnectionError } from "../../domain/remote-connection-values"; import { remoteSnapshot } from "./mail-remote-snapshots"; import type { RemoteRevisionRow, RemoteRow } from "./remote-connection-persistence.mapper"; import { RemoteConnectionPersistenceMapper } from "./remote-connection-persistence.mapper"; import { writeSnapshotRow } from "./scalar-snapshot-writer"; import type { TeleworkRevisionRow, TeleworkRow } from "./telework-authorization-persistence.mapper"; import { TeleworkAuthorizationPersistenceMapper, teleworkIdentity, } from "./telework-authorization-persistence.mapper"; export class SequelizeRemoteConnectionRepository implements IRemoteConnectionRepository { private readonly mapper = new RemoteConnectionPersistenceMapper(); constructor( private readonly database: Sequelize, private readonly inventory: IWorkProfileInventoryService, private readonly resources: IResourceInventoryService ) {} private async load( company: UniqueID, transaction: Transaction, write: boolean ): Promise> { const companyId = company.toPrimitive(); const companies = await this.database.query<{ id: string }>( `SELECT id FROM companies WHERE id=:companyId${write ? " FOR UPDATE" : ""}`, { replacements: { companyId }, transaction, type: QueryTypes.SELECT } ); if (!companies.length) { return Result.fail(new RemoteConnectionError("COMPANY_NOT_FOUND")); } const rows = await this.database.query( "SELECT * FROM remote_connections WHERE companyId=:companyId", { replacements: { companyId }, transaction, type: QueryTypes.SELECT } ); const revisions = await this.database.query( "SELECT r.* FROM remote_connection_revisions r JOIN remote_connections c ON c.id=r.connectionId WHERE c.companyId=:companyId", { replacements: { companyId }, transaction, type: QueryTypes.SELECT } ); const people = await this.database.query<{ id: string; active: boolean }>( "SELECT id,active FROM company_contacts WHERE companyId=:companyId", { replacements: { companyId }, transaction, type: QueryTypes.SELECT } ); const sites = await this.database.query<{ id: string; active: boolean }>( "SELECT id,active FROM company_sites WHERE companyId=:companyId", { replacements: { companyId }, transaction, type: QueryTypes.SELECT } ); const authorizations = await this.database.query( "SELECT * FROM telework_authorizations WHERE companyId=:companyId", { replacements: { companyId }, transaction, type: QueryTypes.SELECT } ); const authRevisions = await this.database.query( "SELECT r.* FROM telework_authorization_revisions r JOIN telework_authorizations a ON a.id=r.authorizationId WHERE a.companyId=:companyId", { replacements: { companyId }, transaction, type: QueryTypes.SELECT } ); const actors = await this.database.query<{ id: string; name: string }>( "SELECT id,name FROM users", { transaction, type: QueryTypes.SELECT } ); const context: RemoteContext = { connections: rows.map((row) => this.mapper.toDomain( row, revisions.filter((r) => r.connectionId === row.id) ) ), authorizations: authorizations.map((row) => new TeleworkAuthorizationPersistenceMapper().toDomain( row, authRevisions.filter((r) => r.authorizationId === row.id) ) ), people: people.map((p) => ({ id: teleworkIdentity(p.id), active: Boolean(p.active) })), sites: sites.map((s) => ({ id: teleworkIdentity(s.id), active: Boolean(s.active) })), assets: await this.inventory.list(company), resources: await this.resources.list(company), actors: new Map(actors.map((a) => [a.id, a.name])), today: new Date().toISOString().slice(0, 10), }; if ( context.connections.some((item) => !remoteReferences(item.values, context)) || context.authorizations.some( (item) => !context.people.some((p) => p.id.equals(item.values.personId)) ) ) { throw new Error("Ámbito de acceso remoto persistido inconsistente."); } return Result.ok(context); } get(company: UniqueID) { return this.database.transaction((t) => this.load(company, t, false)); } save(company: UniqueID, action: Parameters[1]) { return this.database.transaction(async (transaction) => { const loaded = await this.load(company, transaction, true); if (!loaded.isSuccess) { return loaded; } const candidate = action(loaded.data); if (!candidate.isSuccess) { return candidate; } const previous = loaded.data.connections.find((item) => item.id.equals(candidate.data.id)); if (previous !== candidate.data) { const record = this.mapper.toPersistence(candidate.data); await writeSnapshotRow( this.database, "remote_connections", remoteSnapshot, record.root, transaction, previous ? "update" : "insert" ); const stored = new Set(previous?.history.map((r) => r.id.toPrimitive())); for (const revision of record.revisions.filter((r) => !stored.has(r.id))) { await writeSnapshotRow( this.database, "remote_connection_revisions", remoteSnapshot, revision, transaction, "insert" ); } } return Result.ok({ connection: candidate.data, context: loaded.data }); }); } }