collabSupport/services/callTest/storeResolver.js
Joseph McQueen 7aa8c37d1d Add Twilio /calltest for store and direct-dial voice path testing.
Enables outbound PSTN probes via TwiML webhooks with Webex result cards, status polling, and optional store CDR enrichment.
2026-07-23 17:59:10 -04:00

81 lines
2.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// services/callTest/storeResolver.js
// Lightweight store main number lookup (same chain as phoneService, no Meraki fan-out).
import webex from '../../integrations/webex/WebexClient.js';
import { logger } from '../../utils/logger.js';
async function getPersonIdByEmail(email) {
if (!email) return null;
try {
const response = await webex.request('GET', 'people', null, { email });
const people = response.items || [];
return people.length ? people[0].id : null;
} catch (err) {
logger('calltest:resolver', `Person lookup failed: ${err.message}`, 'warn');
return null;
}
}
async function getDectNetworksForPerson(personId) {
if (!personId) return [];
try {
const response = await webex.request('GET', `telephony/config/people/${personId}/dectNetworks`);
const networks = response.dectNetworks || [];
return networks.map((net) => ({
id: net.id,
locationName: net.location?.name || '—',
locationId: net.location?.id || '—',
}));
} catch (err) {
logger('calltest:resolver', `DECT network lookup failed: ${err.message}`, 'warn');
return [];
}
}
/**
* Resolve the store's public main number (auto-attendant DID).
*
* @param {string} storeNum 24 digit store number
*/
export async function resolveStoreMainNumber(storeNum) {
const store = String(storeNum).trim();
const padded = store.padStart(5, '0');
const email = `ae${padded}@ae.com`;
logger('calltest:resolver', `Resolving main number for store ${store} (${email})`, 'debug');
const personId = await getPersonIdByEmail(email);
if (!personId) {
throw new Error(`No Webex person found for store ${store} (${email})`);
}
const dectNets = await getDectNetworksForPerson(personId);
if (!dectNets.length) {
throw new Error(`No DECT network / location found for store ${store}`);
}
const net = dectNets[0];
const locationId = net.locationId && net.locationId !== '—' ? net.locationId : null;
const locationName = net.locationName && net.locationName !== '—' ? net.locationName : null;
if (!locationId) {
throw new Error(`No locationId on DECT network for store ${store}`);
}
const locationDetails = await webex.request('GET', `telephony/config/locations/${locationId}`).catch(() => null);
const dialNumber = locationDetails?.callingLineId?.phoneNumber
|| locationDetails?.phoneNumber
|| null;
if (!dialNumber) {
throw new Error(`No main number (callingLineId) on location for store ${store}`);
}
return {
storeNum: store,
dialNumber,
personId,
locationId,
locationName,
};
}