import { Injectable, Logger } from '@nestjs/common'; import { Organization, Permission, Position, PositionPermission, Unit, } from '@tria-plc/iamapi-common'; import { DataSource, EntityManager, In } from 'typeorm'; import { EDR_FREIGHT_POSITIONS } from './edr-freight.seed'; const SEED_FLAG = 'SEED_EDR_ORG'; const EDR_ORG_KEY = 'edr_freight'; const EDR_UNIT_KEY = 'edr_freight_app'; /** * Seeds the operational freight positions (CEO, Chief, Director, Marketer, * Operation, Ethiopian GL, Djibouti GL) as Position + PositionPermission rows * on the `edr_freight_app` unit. Positions-as-roles: users get their freight * access by being assigned to a Position (via EmployeePosition), and the * position's PositionPermission grants come from EDR_FREIGHT_POSITIONS. * * Gated behind the same SEED_EDR_ORG flag as EdrOrgSeeder and depends on the * org/unit/permission catalog it seeds, so it must run AFTER EdrOrgSeeder. * Idempotent: positions upsert by (key, unitId); grants insert only the * permission ids a position is still missing. */ @Injectable() export class FreightPositionsSeeder { private readonly logger = new Logger(FreightPositionsSeeder.name); constructor(private readonly dataSource: DataSource) {} async run() { if (process.env[SEED_FLAG]?.trim().toLowerCase() !== 'true') { this.logger.log( `Skipping freight positions seed because ${SEED_FLAG} is not enabled`, ); return; } await this.dataSource.transaction(async (manager) => { const organization = await manager.getRepository(Organization).findOne({ where: { key: EDR_ORG_KEY }, select: { id: true }, }); if (!organization) { throw new Error(`missing_organization:${EDR_ORG_KEY}`); } const unit = await manager.getRepository(Unit).findOne({ where: { key: EDR_UNIT_KEY, organizationId: organization.id }, select: { id: true }, }); if (!unit) { throw new Error(`missing_unit:${EDR_UNIT_KEY}`); } const permissionKeyToId = await this.loadPermissionIds(manager); for (const seed of EDR_FREIGHT_POSITIONS) { const positionId = await this.ensurePosition( manager, seed, unit.id as string, organization.id as string, ); await this.ensurePositionPermissions( manager, positionId, seed, permissionKeyToId, ); } }); this.logger.log( `Ensured ${EDR_FREIGHT_POSITIONS.length} freight positions on unit '${EDR_UNIT_KEY}'`, ); } /** Resolve every permission key referenced by any position to its id. */ private async loadPermissionIds( manager: EntityManager, ): Promise> { const keys = [ ...new Set(EDR_FREIGHT_POSITIONS.flatMap((p) => p.permissionKeys)), ]; const permissions = await manager.getRepository(Permission).find({ where: { key: In(keys) }, select: { id: true, key: true }, }); const map = new Map(permissions.map((p) => [p.key, p.id as string])); const missing = keys.filter((key) => !map.has(key)); if (missing.length > 0) { throw new Error(`missing_permissions:${missing.join(',')}`); } return map; } private async ensurePosition( manager: EntityManager, seed: (typeof EDR_FREIGHT_POSITIONS)[number], unitId: string, organizationId: string, ): Promise { const positionRepository = manager.getRepository(Position); const existing = await positionRepository.findOne({ where: { key: seed.key, unitId }, select: { id: true }, }); if (existing) { return existing.id as string; } const inserted = await positionRepository.insert({ key: seed.key, name: { ...seed.name }, rank: seed.rank, unitId, organizationId, }); this.logger.log(`Seeded freight position '${seed.key}'`); return inserted.identifiers[0]?.id as string; } private async ensurePositionPermissions( manager: EntityManager, positionId: string, seed: (typeof EDR_FREIGHT_POSITIONS)[number], permissionKeyToId: Map, ) { const positionPermissionRepository = manager.getRepository(PositionPermission); const existing = await positionPermissionRepository.find({ where: { positionId }, select: { permissionId: true }, }); const existingPermissionIds = new Set( existing.map((row) => row.permissionId), ); const rowsToInsert = seed.permissionKeys .map((key) => permissionKeyToId.get(key) as string) .filter((permissionId) => !existingPermissionIds.has(permissionId)) .map((permissionId) => ({ positionId, permissionId })); if (rowsToInsert.length === 0) { return; } await positionPermissionRepository.insert(rowsToInsert); this.logger.log( `Granted ${rowsToInsert.length} permissions to position '${seed.key}'`, ); } }