metis-desk/modules/companies/src/api/infrastructure/persistence/sequelize-folder-mapping.repository.ts

56 lines
5.4 KiB
TypeScript
Raw Normal View History

2026-10-05 10:02:04 +00:00
import type {UniqueID} from "@repo/rdx-ddd";
import {Result} from "@repo/rdx-utils";
import {QueryTypes,type Sequelize,type Transaction} from "sequelize";
import type {FolderContext,IFolderMappingRepository} from "../../application/folder-mapping.repository";
import type {IWorkProfileInventoryService} from "../../application/work-profile.repository";
import {folderReferences} from "../../application/folder-mapping-policy";
import {AccessDefinitionSchema} from "../../domain/access/access-definition";
import {AccessValues} from "../../domain/access/access-values";
import {FolderMappingError} from "../../domain/folder-mapping-values";
import {FolderMappingPersistenceMapper,type FolderRow,type FolderRevisionRow} from "./folder-mapping-persistence.mapper";
import {WorkProfilePersistenceMapper,type ProfileRow,type ProfileRevisionRow,profileIdentity,persistedJson} from "./work-profile-persistence.mapper";
export class SequelizeFolderMappingRepository implements IFolderMappingRepository{
private readonly mapper=new FolderMappingPersistenceMapper();
constructor(private readonly database:Sequelize,private readonly inventory:IWorkProfileInventoryService){}
private async load(company:UniqueID,transaction:Transaction,write:boolean):Promise<Result<FolderContext,FolderMappingError>>{
const replacements={companyId:company.toPrimitive()},options={replacements,transaction,type:QueryTypes.SELECT};
const companies=await this.database.query<{id:string}>(`SELECT id FROM companies WHERE id=:companyId${write ? " FOR UPDATE":""}`,{...options,type:QueryTypes.SELECT});
if(!companies.length){return Result.fail(new FolderMappingError("COMPANY_NOT_FOUND"));}
const rows=await this.database.query<FolderRow>("SELECT * FROM folder_mappings WHERE companyId=:companyId",{...options,type:QueryTypes.SELECT});
const revisions=await this.database.query<FolderRevisionRow>("SELECT r.* FROM folder_mapping_revisions r JOIN folder_mappings m ON m.id=r.mappingId WHERE m.companyId=:companyId",{...options,type:QueryTypes.SELECT});
const profiles=await this.database.query<ProfileRow>("SELECT * FROM work_profiles WHERE companyId=:companyId",{...options,type:QueryTypes.SELECT});
const profileRevisions=await this.database.query<ProfileRevisionRow>("SELECT r.* FROM work_profile_revisions r JOIN work_profiles p ON p.id=r.profileId WHERE p.companyId=:companyId",{...options,type:QueryTypes.SELECT});
const items=await this.database.query<{id:string;values:unknown}>("SELECT i.id,i.values FROM access_items i JOIN access_workspaces w ON w.id=i.workspaceId WHERE w.companyId=:companyId OR w.companyId IS NULL",{...options,type:QueryTypes.SELECT});
const people=await this.database.query<{id:string;active:boolean}>("SELECT id,active FROM company_contacts WHERE companyId=:companyId",{...options,type:QueryTypes.SELECT});
const actors=await this.database.query<{id:string;name:string}>("SELECT id,name FROM users",{transaction,type:QueryTypes.SELECT});
const context:FolderContext={mappings:rows.map(row=>this.mapper.toDomain(row,revisions.filter(r=>r.mappingId===row.id))),
profiles:profiles.map(row=>new WorkProfilePersistenceMapper().toDomain(row,profileRevisions.filter(r=>r.profileId===row.id))),
items:items.map(item=>{const values=AccessValues.create(AccessDefinitionSchema.parse(persistedJson(item.values)));
if(!values.isSuccess){throw new Error("Recurso de carpeta persistido inconsistente.");}
return {id:profileIdentity(item.id),values:values.data};}),
people:people.map(item=>({id:profileIdentity(item.id),active:Boolean(item.active)})),assets:await this.inventory.list(company),
actors:new Map(actors.map(actor=>[actor.id,actor.name])),today:new Date().toISOString().slice(0,10)};
if(context.profiles.some(profile=>!context.people.some(person=>person.id.equals(profile.values.personId)) ||
!context.assets.some(asset=>asset.id.equals(profile.values.assetId))) ||
context.mappings.some(mapping=>!folderReferences(mapping.values,context))){throw new Error("Ámbito de carpeta persistido inconsistente.");}
return Result.ok(context);
}
get(company:UniqueID){return this.database.transaction(t=>this.load(company,t,false));}
save(company:UniqueID,action:Parameters<IFolderMappingRepository["save"]>[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.mappings.find(item=>item.id.equals(candidate.data.id));
if(previous!==candidate.data){
const record=this.mapper.toPersistence(candidate.data);
await this.database.query("INSERT INTO folder_mappings (id,companyId,profileId,needId,`values`,currentKey,driveKey) VALUES (:id,:companyId,:profileId,:needId,:values,:currentKey,:driveKey) ON DUPLICATE KEY UPDATE `values`=VALUES(`values`),currentKey=VALUES(currentKey),driveKey=VALUES(driveKey)",{replacements:record.root,transaction});
const stored=new Set(previous?.history.map(r=>r.id.toPrimitive()));
for(const revision of record.revisions.filter(r=>!stored.has(r.id))){
await this.database.query("INSERT INTO folder_mapping_revisions (id,mappingId,position,actorId,recordedAt,`values`) VALUES (:id,:mappingId,:position,:actorId,:recordedAt,:values)",{replacements:revision,transaction});
}
}
return Result.ok({mapping:candidate.data,context:loaded.data});
});
}
}