import type { UniqueID } from "@repo/rdx-ddd"; import { Result } from "@repo/rdx-utils"; import type { Sequelize, Transaction } from "sequelize"; import { QueryTypes } from "sequelize"; import { z } from "zod/v4"; import type { IInfrastructureRepository } from "../../application/infrastructure.repository"; import { AssetCatalogError } from "../../domain/asset-definition"; import { writeAssetRow } from "./asset-relational-sql"; import { AddressRowSchema, InfrastructurePersistenceMapper, InfrastructureRevisionRowSchema, InfrastructureRoleRowSchema, InfrastructureRootRowSchema, InterfaceRowSchema, } from "./infrastructure-persistence.mapper"; import { writeInfrastructureRevision } from "./write-infrastructure-revision"; export class SequelizeInfrastructureRepository implements IInfrastructureRepository { constructor(private readonly database: Sequelize) {} private async load(company: UniqueID, transaction: Transaction, lock = false) { const companies = await this.database.query<{ id: string }>( `SELECT id FROM companies WHERE id=:id${lock ? " FOR UPDATE" : ""}`, { replacements: { id: company.toPrimitive() }, type: QueryTypes.SELECT, transaction } ); if (!companies.length) { return Result.fail(new AssetCatalogError("COMPANY_NOT_FOUND")); } const select = async (table: string) => this.database.query( `SELECT t.* FROM ${table} t JOIN asset_infrastructure_revisions r ON r.id=t.revisionId JOIN asset_infrastructure i ON i.id=r.infrastructureId WHERE i.companyId=:companyId`, { replacements: { companyId: company.toPrimitive() }, type: QueryTypes.SELECT, transaction } ); const roots = z.array(InfrastructureRootRowSchema).parse( await this.database.query("SELECT * FROM asset_infrastructure WHERE companyId=:companyId", { replacements: { companyId: company.toPrimitive() }, type: QueryTypes.SELECT, transaction, }) ); const revisions = z.array(InfrastructureRevisionRowSchema).parse( await this.database.query( "SELECT r.* FROM asset_infrastructure_revisions r JOIN asset_infrastructure i ON i.id=r.infrastructureId WHERE i.companyId=:companyId", { replacements: { companyId: company.toPrimitive() }, type: QueryTypes.SELECT, transaction, } ) ); const roles = z .array(InfrastructureRoleRowSchema) .parse(await select("asset_infrastructure_roles")); const interfaces = z.array(InterfaceRowSchema).parse(await select("asset_interface_snapshots")); const addresses = z .array(AddressRowSchema) .parse(await select("asset_interface_address_snapshots")); const actors = z.array(z.object({ id: z.uuid(), name: z.string() })).parse( await this.database.query("SELECT id,name FROM users", { type: QueryTypes.SELECT, transaction, }) ); const mapper = new InfrastructurePersistenceMapper(); return Result.ok({ items: roots.map((root) => mapper.toDomain(root, revisions, roles, interfaces, addresses)), actorNames: new Map(actors.map((actor) => [actor.id, actor.name])), }); } get(company: UniqueID) { return this.database.transaction((transaction) => this.load(company, transaction)); } save( company: UniqueID, asset: UniqueID, change: Parameters[2] ) { return this.database.transaction(async (transaction) => { const loaded = await this.load(company, transaction, true); if (!loaded.isSuccess) { return Result.fail(loaded.error); } const assets = await this.database.query<{ id: string }>( "SELECT id FROM assets WHERE companyId=:companyId AND id=:assetId FOR UPDATE", { replacements: { companyId: company.toPrimitive(), assetId: asset.toPrimitive() }, type: QueryTypes.SELECT, transaction, } ); if (!assets.length) { return Result.fail(new AssetCatalogError("ASSET_NOT_FOUND")); } const previous = loaded.data.items.find((item) => item.assetId.equals(asset)); const changed = change(previous); if (!changed.isSuccess) { return Result.fail(changed.error); } const item = changed.data; if (!previous) { await writeAssetRow( this.database, "asset_infrastructure", { id: item.id.toPrimitive(), companyId: company.toPrimitive(), assetId: asset.toPrimitive(), }, transaction ); } for (const [position, revision] of item.history.entries()) { if (previous?.history.some((old) => old.id.equals(revision.id))) { continue; } await writeInfrastructureRevision( this.database, item.id.toPrimitive(), revision, position, transaction ); } return Result.ok({ item, actorNames: loaded.data.actorNames }); }); } }