From 6283a358a873e46626549a9173ac39522d181fc6 Mon Sep 17 00:00:00 2001 From: Zach Raymer Date: Mon, 9 Mar 2026 12:30:33 -0500 Subject: [PATCH 1/2] feat: dial number validation uses dial number profile regex and retains US format for fallback --- packages/@webex/contact-center/src/cc.ts | 6 +- .../contact-center/src/services/core/Utils.ts | 35 ++++++-- .../test/unit/spec/services/core/Utils.ts | 87 ++++++++++++++++++- 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/packages/@webex/contact-center/src/cc.ts b/packages/@webex/contact-center/src/cc.ts index 41292d68e6a..68973628bfa 100644 --- a/packages/@webex/contact-center/src/cc.ts +++ b/packages/@webex/contact-center/src/cc.ts @@ -785,7 +785,11 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter METRIC_EVENT_NAMES.STATION_LOGIN_FAILED, ]); - if (data.loginOption === LoginOption.AGENT_DN && !isValidDialNumber(data.dialNumber)) { + const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? []; + if ( + data.loginOption === LoginOption.AGENT_DN && + !isValidDialNumber(data.dialNumber, dialPlanEntries) + ) { const error = new Error('INVALID_DIAL_NUMBER'); // @ts-ignore - adding custom key to the error object error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure; diff --git a/packages/@webex/contact-center/src/services/core/Utils.ts b/packages/@webex/contact-center/src/services/core/Utils.ts index 8e439607933..ebfacd02850 100644 --- a/packages/@webex/contact-center/src/services/core/Utils.ts +++ b/packages/@webex/contact-center/src/services/core/Utils.ts @@ -10,6 +10,7 @@ import { Interaction, } from '../task/types'; import {PARTICIPANT_TYPES, STATE_CONSULT} from './constants'; +import {DialPlan} from '../config/types'; /** * Extracts common error details from a Webex request payload. @@ -48,11 +49,35 @@ const getAgentActionTypeFromTask = (taskData?: TaskData): 'DIAL_NUMBER' | '' => return isDialNumber || isEntryPointVariant ? 'DIAL_NUMBER' : ''; }; -export const isValidDialNumber = (input: string): boolean => { - // This regex checks for a valid dial number format for only few countries such as US, Canada. - const regexForDn = /1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}/; +// Fallback regex for US/Canada dial numbers when no dial plan entries are configured +const FALLBACK_DIAL_NUMBER_REGEX = /1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}/; - return regexForDn.test(input); +/** + * Validates a dial number against the provided dial plan regex patterns. + * A number is valid if it matches at least one regex pattern in the dial plans. + * Falls back to US/Canada regex validation if no dial plan entries are configured. + * + * @param input - The dial number to validate + * @param dialPlanEntries - Array of dial plan entries containing regex patterns + * @returns true if the input matches at least one dial plan regex pattern, false otherwise + */ +export const isValidDialNumber = ( + input: string, + dialPlanEntries: DialPlan['dialPlanEntity'] +): boolean => { + if (!dialPlanEntries || dialPlanEntries.length === 0) { + return FALLBACK_DIAL_NUMBER_REGEX.test(input); + } + + return dialPlanEntries.some((entry) => { + try { + const regex = new RegExp(entry.regex); + + return regex.test(input); + } catch { + return false; + } + }); }; export const getStationLoginErrorData = (failure: Failure, loginOption: LoginOption) => { @@ -74,7 +99,7 @@ export const getStationLoginErrorData = (failure: Failure, loginOption: LoginOpt }, INVALID_DIAL_NUMBER: { message: - 'Enter a valid US dial number. For help, reach out to your administrator or support team.', + 'Enter a valid dial number. For help, reach out to your administrator or support team.', fieldName: loginOption, }, }; diff --git a/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts b/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts index 35c0e895574..779849c34dd 100644 --- a/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts +++ b/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts @@ -244,7 +244,7 @@ describe('Utils', () => { const result = Utils.getStationLoginErrorData(failure, LoginOption.AGENT_DN); expect(result).toEqual({ message: - 'Enter a valid US dial number. For help, reach out to your administrator or support team.', + 'Enter a valid dial number. For help, reach out to your administrator or support team.', fieldName: LoginOption.AGENT_DN, }); }); @@ -557,4 +557,89 @@ describe('Utils', () => { }); }); + describe('isValidDialNumber', () => { + const anyFormatEntry = { + name: 'Any Format', + prefix: '', + regex: '([0-9a-zA-Z]+[-._])*[0-9a-zA-Z]+', + strippedChars: '( )-', + }; + + const usOnlyEntry = { + name: 'US', + prefix: '1', + regex: '1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}', + strippedChars: '( )-', + }; + + describe('with multiple dial plan entries (Any Format + US)', () => { + const dialPlanEntries = [anyFormatEntry, usOnlyEntry]; + + it('should return true for a valid US phone number', () => { + const result = Utils.isValidDialNumber('12223334567', dialPlanEntries); + expect(result).toBe(true); + }); + + it('should return true for a UK phone number', () => { + const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries); + expect(result).toBe(true); + }); + + it('should return true for alphanumeric format', () => { + const result = Utils.isValidDialNumber('user.name', dialPlanEntries); + expect(result).toBe(true); + }); + }); + + describe('with US-only dial plan entry', () => { + const dialPlanEntries = [usOnlyEntry]; + + it('should return true for a valid US phone number', () => { + const result = Utils.isValidDialNumber('12223334567', dialPlanEntries); + expect(result).toBe(true); + }); + + it('should return false for a UK phone number', () => { + const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries); + expect(result).toBe(false); + }); + + it('should return false for an invalid US number format', () => { + const result = Utils.isValidDialNumber('1234567890', dialPlanEntries); + expect(result).toBe(false); + }); + }); + + describe('with empty dial plan entries (fallback to US regex)', () => { + it('should return true for a valid US phone number', () => { + const result = Utils.isValidDialNumber('12223334567', []); + expect(result).toBe(true); + }); + + it('should return false for a UK phone number', () => { + const result = Utils.isValidDialNumber('+442030484377', []); + expect(result).toBe(false); + }); + }); + + describe('with invalid regex in dial plan entry', () => { + const invalidRegexEntry = { + name: 'Invalid', + prefix: '', + regex: '[invalid(regex', + strippedChars: '', + }; + + it('should return false when regex is invalid and no other entries match', () => { + const result = Utils.isValidDialNumber('12223334567', [invalidRegexEntry]); + expect(result).toBe(false); + }); + + it('should return true if another valid entry matches', () => { + const result = Utils.isValidDialNumber('12223334567', [invalidRegexEntry, usOnlyEntry]); + expect(result).toBe(true); + }); + }); + }); + }); From 3b3c262d0be05106e880740774e305c061032c86 Mon Sep 17 00:00:00 2001 From: Zach Raymer Date: Tue, 10 Mar 2026 09:46:14 -0500 Subject: [PATCH 2/2] chore: updates tests for dial validation and other Pr comments --- .../contact-center/src/services/core/Utils.ts | 4 ++- .../test/unit/spec/services/core/Utils.ts | 27 ++----------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/packages/@webex/contact-center/src/services/core/Utils.ts b/packages/@webex/contact-center/src/services/core/Utils.ts index ebfacd02850..c6b99b9589a 100644 --- a/packages/@webex/contact-center/src/services/core/Utils.ts +++ b/packages/@webex/contact-center/src/services/core/Utils.ts @@ -50,7 +50,7 @@ const getAgentActionTypeFromTask = (taskData?: TaskData): 'DIAL_NUMBER' | '' => }; // Fallback regex for US/Canada dial numbers when no dial plan entries are configured -const FALLBACK_DIAL_NUMBER_REGEX = /1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}/; +export const FALLBACK_DIAL_NUMBER_REGEX = /1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}/; /** * Validates a dial number against the provided dial plan regex patterns. @@ -66,6 +66,8 @@ export const isValidDialNumber = ( dialPlanEntries: DialPlan['dialPlanEntity'] ): boolean => { if (!dialPlanEntries || dialPlanEntries.length === 0) { + LoggerProxy.info('No dial plan entries found. Falling back to US number validation.'); + return FALLBACK_DIAL_NUMBER_REGEX.test(input); } diff --git a/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts b/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts index 779849c34dd..ac0543c415d 100644 --- a/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts +++ b/packages/@webex/contact-center/test/unit/spec/services/core/Utils.ts @@ -1,4 +1,5 @@ import * as Utils from '../../../../../src/services/core/Utils'; +import {FALLBACK_DIAL_NUMBER_REGEX} from '../../../../../src/services/core/Utils'; import LoggerProxy from '../../../../../src/logger-proxy'; import WebexRequest from '../../../../../src/services/core/WebexRequest'; import {LoginOption, WebexRequestPayload} from '../../../../../src/types'; @@ -568,7 +569,7 @@ describe('Utils', () => { const usOnlyEntry = { name: 'US', prefix: '1', - regex: '1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}', + regex: FALLBACK_DIAL_NUMBER_REGEX.source, strippedChars: '( )-', }; @@ -584,11 +585,6 @@ describe('Utils', () => { const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries); expect(result).toBe(true); }); - - it('should return true for alphanumeric format', () => { - const result = Utils.isValidDialNumber('user.name', dialPlanEntries); - expect(result).toBe(true); - }); }); describe('with US-only dial plan entry', () => { @@ -621,25 +617,6 @@ describe('Utils', () => { expect(result).toBe(false); }); }); - - describe('with invalid regex in dial plan entry', () => { - const invalidRegexEntry = { - name: 'Invalid', - prefix: '', - regex: '[invalid(regex', - strippedChars: '', - }; - - it('should return false when regex is invalid and no other entries match', () => { - const result = Utils.isValidDialNumber('12223334567', [invalidRegexEntry]); - expect(result).toBe(false); - }); - - it('should return true if another valid entry matches', () => { - const result = Utils.isValidDialNumber('12223334567', [invalidRegexEntry, usOnlyEntry]); - expect(result).toBe(true); - }); - }); }); });