import { NotificationRecipients } from "@edr/types"; import { Injectable, Logger } from "@nestjs/common"; import { BackofficeService } from "../backoffice/backoffice.service"; import { CompanyProfileRepository } from "../companies/company-profile.repository"; import { ExternalProfileRepository } from "../companies/external-profile.repository"; /** * Turns a {@link NotificationRecipients} selector into a de-duplicated set of * IAM user ids. * * - `userIds` → honored as-is. * - `companyId` → all portal users linked to the company (external_profiles). * - `companyProfileId` → resolved to its company, then to that company's users. * - `organizationId` → all current employees of the org (backoffice staff). * - `permissionKeys` → current employees (any org) holding any of these * permission keys — how every staff-facing notification is targeted. There is * deliberately no "all backoffice" selector: staff notifications belong to a * desk, and the `:get_notification` keys name which one. */ @Injectable() export class NotificationRecipientsService { private readonly logger = new Logger(NotificationRecipientsService.name); constructor( private readonly externalProfiles: ExternalProfileRepository, private readonly companyProfiles: CompanyProfileRepository, private readonly backoffice: BackofficeService, ) {} async resolve(recipients: NotificationRecipients): Promise { const ids = new Set(); for (const id of recipients.userIds ?? []) { if (id) ids.add(id); } let companyId = recipients.companyId; if (!companyId && recipients.companyProfileId) { const profile = await this.companyProfiles.findById( recipients.companyProfileId, ); companyId = profile?.companyId ?? undefined; } if (companyId) { const profiles = await this.externalProfiles.findByCompanyId(companyId); for (const p of profiles) { if (p.userId) ids.add(p.userId); } } if (recipients.organizationId) { try { const { items } = await this.backoffice.getOrganizationEmployees( recipients.organizationId, {}, ); for (const employee of items as Array<{ user?: { id?: string }; userId?: string; }>) { const uid = employee?.user?.id ?? employee?.userId; if (uid) ids.add(uid); } } catch (err) { this.logger.warn( `Failed to resolve org recipients for ${recipients.organizationId}: ${ (err as Error).message }`, ); } } if (recipients.permissionKeys?.length) { try { for (const uid of await this.backoffice.getEmployeeUserIdsByPermission( recipients.permissionKeys, )) { ids.add(uid); } } catch (err) { this.logger.warn( `Failed to resolve permissionKeys recipients: ${(err as Error).message}`, ); } } return [...ids]; } }