mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
feat(calls): consolidate agent identity via Ozonetel CDR
Ozonetel's webhook AgentName is a transfer-chain display string — same display can collide (two agents both named "GlobalHealthX" with distinct agent IDs), and chained like "RamaiahAdmin -> Ganesh Bandi -> GlobalHealthX". Team Performance was bucketing every unique raw string as a separate "agent", producing 7 rows for 3 real agents. Fix — authoritative agent link via CDR AgentID (unique): - New AgentLookupService (platform module): case-insensitive ozonetelAgentId → Agent UUID cache, shared across webhook / dispose / enrichment / backfill paths - Webhook + outbound-dispose now persist UCID on Call so CDR can join - Outbound dispose resolves agent relation at create time and overwrites from CDR AgentID post-hoc (catches dial transfers) - New CdrEnrichmentService: every 30 min fetches today + yesterday CDR, patches Calls missing agentId / transferredTo / transferType by UCID join. Well under Ozonetel's 2 req/min cap. - Historical backfill maint endpoint: /api/maint/enrich-call-agents with configurable day window (default 2, max 15). Rate-limited at 35s between dates. Call schema additions (synced on Global + Ramaiah): agent relation, ucid, transferredTo, transferType. agentName remains for legacy/display. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
54
src/platform/agent-lookup.service.ts
Normal file
54
src/platform/agent-lookup.service.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
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<string, string>();
|
||||
|
||||
constructor(private readonly platform: PlatformGraphqlService) {}
|
||||
|
||||
async onModuleInit() {
|
||||
await this.refresh();
|
||||
}
|
||||
|
||||
async refresh(): Promise<void> {
|
||||
try {
|
||||
const data = await this.platform.query<any>(
|
||||
`{ agents(first: 100) { edges { node { id ozonetelAgentId } } } }`,
|
||||
);
|
||||
const edges = data?.agents?.edges ?? [];
|
||||
this.uuidByOzonetelId.clear();
|
||||
for (const edge of edges) {
|
||||
const n = edge.node;
|
||||
if (n.ozonetelAgentId) {
|
||||
this.uuidByOzonetelId.set(n.ozonetelAgentId.toLowerCase(), n.id);
|
||||
}
|
||||
}
|
||||
this.logger.log(`[AGENT-LOOKUP] Loaded ${this.uuidByOzonetelId.size} agents`);
|
||||
} catch (err) {
|
||||
this.logger.warn(`[AGENT-LOOKUP] Refresh failed: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async resolveByOzonetelId(ozonetelId: string | null | undefined): Promise<string | null> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PlatformGraphqlService } from './platform-graphql.service';
|
||||
import { AgentLookupService } from './agent-lookup.service';
|
||||
|
||||
@Module({
|
||||
providers: [PlatformGraphqlService],
|
||||
exports: [PlatformGraphqlService],
|
||||
providers: [PlatformGraphqlService, AgentLookupService],
|
||||
exports: [PlatformGraphqlService, AgentLookupService],
|
||||
})
|
||||
export class PlatformModule {}
|
||||
|
||||
Reference in New Issue
Block a user