import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { PlatformGraphqlService } from './platform-graphql.service'; /** * Maps Ozonetel agent identifiers (unique — e.g. "ramaiahadmin", * "globalhealthx", "global") to the platform Agent entity UUID. Used by * ingest paths (webhook, dispose, CDR enrichment, backfill) so every Call * ends up with the correct `agent` relation regardless of how Ozonetel * formats the display name (AgentName collisions, transfer chains like * "A -> B -> C", etc.). * * The cache is case-insensitive because Ozonetel occasionally mixes * casing ("global" vs "Global" vs "GLOBAL") across webhook/CDR responses. */ @Injectable() export class AgentLookupService implements OnModuleInit { private readonly logger = new Logger(AgentLookupService.name); private readonly uuidByOzonetelId = new Map(); private readonly uuidByDisplayName = new Map(); constructor(private readonly platform: PlatformGraphqlService) {} async onModuleInit() { await this.refresh(); } async refresh(): Promise { try { const data = await this.platform.query( `{ agents(first: 100) { edges { node { id ozonetelAgentId ozonetelDisplayName } } } }`, ); const edges = data?.agents?.edges ?? []; this.uuidByOzonetelId.clear(); this.uuidByDisplayName.clear(); for (const edge of edges) { const n = edge.node; if (n.ozonetelAgentId) { this.uuidByOzonetelId.set(n.ozonetelAgentId.toLowerCase(), n.id); } if (n.ozonetelDisplayName) { this.uuidByDisplayName.set(n.ozonetelDisplayName.toLowerCase().trim(), n.id); } } this.logger.log(`[AGENT-LOOKUP] Loaded ${this.uuidByOzonetelId.size} agents (${this.uuidByDisplayName.size} with display name)`); } catch (err) { this.logger.warn(`[AGENT-LOOKUP] Refresh failed: ${err}`); } } async resolveByOzonetelId(ozonetelId: string | null | undefined): Promise { if (!ozonetelId) return null; const key = ozonetelId.toLowerCase(); const cached = this.uuidByOzonetelId.get(key); if (cached) return cached; // Cache miss — refresh once (handles late-provisioned agents) await this.refresh(); return this.uuidByOzonetelId.get(key) ?? null; } // Resolve by Ozonetel display name (e.g. "Ganesh Bandi") — used by // missed-call webhook backfill where only AgentName (display) is available. async resolveByDisplayName(displayName: string | null | undefined): Promise { if (!displayName) return null; const key = displayName.toLowerCase().trim(); const cached = this.uuidByDisplayName.get(key); if (cached) return cached; await this.refresh(); return this.uuidByDisplayName.get(key) ?? null; } }