diff --git a/src/maint/maint.controller.ts b/src/maint/maint.controller.ts index e2cb8b9..df6d207 100644 --- a/src/maint/maint.controller.ts +++ b/src/maint/maint.controller.ts @@ -611,16 +611,21 @@ export class MaintController { this.logger.log('[MAINT] Backfill call agents by name — matching agentName last-segment to Agent entity'); // Pull all active agents — cheap, cached at service level but we - // also need a name→UUID map for this pass. + // also need name → UUID maps for this pass. Three indexes: + // - ozonetelAgentId (e.g. "globalhealthx") — matches outbound dispose rows + // - ozonetelDisplayName (e.g. "Ganesh Bandi") — matches inbound webhook rows + // - platform Agent.name (e.g. "Ganesh Iyer") — last-resort fallback const agentData = await this.platform.query( - `{ agents(first: 100) { edges { node { id name ozonetelAgentId } } } }`, + `{ agents(first: 100) { edges { node { id name ozonetelAgentId ozonetelDisplayName } } } }`, ); - const agentUuidByName = new Map(); // lowercase name → UUID - const agentUuidByOzonetelId = new Map(); // lowercase ozonetelAgentId → UUID + const agentUuidByName = new Map(); + const agentUuidByOzonetelId = new Map(); + const agentUuidByDisplayName = new Map(); for (const edge of agentData?.agents?.edges ?? []) { const a = edge.node; if (a.name) agentUuidByName.set(a.name.toLowerCase().trim(), a.id); if (a.ozonetelAgentId) agentUuidByOzonetelId.set(a.ozonetelAgentId.toLowerCase().trim(), a.id); + if (a.ozonetelDisplayName) agentUuidByDisplayName.set(a.ozonetelDisplayName.toLowerCase().trim(), a.id); } let scanned = 0; @@ -654,9 +659,14 @@ export class MaintController { const last = segments[segments.length - 1]; if (!last) { skipped++; continue; } - // Prefer ozonetelAgentId match; fall back to display name. + // Prefer ozonetelAgentId match (outbound rows store + // agentName=agentId); fall back to ozonetelDisplayName + // (inbound webhook rows store the Ozonetel display string); + // last-resort match on platform Agent.name. const key = last.toLowerCase(); - const uuid = agentUuidByOzonetelId.get(key) ?? agentUuidByName.get(key); + const uuid = agentUuidByOzonetelId.get(key) + ?? agentUuidByDisplayName.get(key) + ?? agentUuidByName.get(key); if (!uuid) { unmatched++; if (unmatchedSamples.size < 10) unmatchedSamples.add(last); diff --git a/src/platform/agent-lookup.service.ts b/src/platform/agent-lookup.service.ts index 121c046..b537421 100644 --- a/src/platform/agent-lookup.service.ts +++ b/src/platform/agent-lookup.service.ts @@ -16,6 +16,7 @@ import { PlatformGraphqlService } from './platform-graphql.service'; 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) {} @@ -26,17 +27,21 @@ export class AgentLookupService implements OnModuleInit { async refresh(): Promise { try { const data = await this.platform.query( - `{ agents(first: 100) { edges { node { id ozonetelAgentId } } } }`, + `{ 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.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}`); } @@ -51,4 +56,15 @@ export class AgentLookupService implements OnModuleInit { 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; + } }