diff --git a/packages/calling/src/SDKConnector/types.ts b/packages/calling/src/SDKConnector/types.ts index 05e80797a1d..840a3f872a2 100644 --- a/packages/calling/src/SDKConnector/types.ts +++ b/packages/calling/src/SDKConnector/types.ts @@ -26,6 +26,29 @@ export type Model = { }; }; +export type WDMDevice = { + url: string; + userId: string; + orgId: string; + version: string; + callingBehavior: string; + registered?: boolean; + webSocketUrl?: string; + register?: () => Promise; + refresh?: () => Promise; + /** WDM / device-registration settings (e.g. `webrtc-calling-over-ws`). */ + settings?: Record; + features: { + developer: { + models: Model[]; + get: (key: string) => {value: boolean} | undefined; + }; + entitlement: { + models: Model[]; + }; + }; +}; + export type ServiceCatalog = { serviceGroups: { // cSpell:disable @@ -99,28 +122,7 @@ export interface WebexSDK { updateFeature?: (featureToggle: unknown) => void; }; calendar: unknown; - device: { - url: string; - userId: string; - orgId: string; - version: string; - callingBehavior: string; - registered?: boolean; - webSocketUrl?: string; - register?: () => Promise; - refresh?: () => Promise; - /** WDM / device-registration settings (e.g. `webrtc-calling-over-ws`). */ - settings?: Record; - features: { - developer: { - models: Model[]; - get: (key: string) => {value: boolean} | undefined; - }; - entitlement: { - models: Model[]; - }; - }; - }; + device: WDMDevice; encryption: { decryptText: (encryptionKeyUrl: string, encryptedData?: string) => Promise; encryptText: (encryptionKeyUrl: string, text?: string) => Promise; diff --git a/packages/calling/src/common/Utils.test.ts b/packages/calling/src/common/Utils.test.ts index 044938c324e..a88fa7b0840 100644 --- a/packages/calling/src/common/Utils.test.ts +++ b/packages/calling/src/common/Utils.test.ts @@ -51,6 +51,8 @@ import { modifySdpForIPv4, uploadLogs, handleCallingClientErrors, + resolveCallingBackend, + getCallingBackEnd, } from './Utils'; import { getVoicemailListJsonWXC, @@ -2081,3 +2083,88 @@ describe('uploadLogs', () => { ); }); }); + +describe('resolveCallingBackend', () => { + const createDevice = (callingBehavior: string, entitlementKeys: string[]) => ({ + url: 'https://wdm.example.com/devices/123', + userId: 'user-id', + orgId: 'org-id', + version: '1.0', + callingBehavior, + features: { + developer: {models: [], get: jest.fn()}, + entitlement: { + models: entitlementKeys.map((key) => ({_values: {key}})), + }, + }, + }); + + it.each([ + ['bc-sp-basic', CALLING_BACKEND.WXC], + ['bc-sp-standard', CALLING_BACKEND.WXC], + ])( + 'returns WXC when callingBehavior is NATIVE_WEBEX_TEAMS_CALLING and entitlement is %s', + (entitlement, expected) => { + const device = createDevice('NATIVE_WEBEX_TEAMS_CALLING', [entitlement]); + + expect(resolveCallingBackend(device)).toBe(expected); + } + ); + + it('returns BWRKS when callingBehavior is NATIVE_WEBEX_TEAMS_CALLING and entitlement is broadworks-connector', () => { + const device = createDevice('NATIVE_WEBEX_TEAMS_CALLING', ['broadworks-connector']); + + expect(resolveCallingBackend(device)).toBe(CALLING_BACKEND.BWRKS); + }); + + it('returns UCM when callingBehavior is NATIVE_SIP_CALL_TO_UCM', () => { + const device = createDevice('NATIVE_SIP_CALL_TO_UCM', []); + + expect(resolveCallingBackend(device)).toBe(CALLING_BACKEND.UCM); + }); + + it('returns INVALID when callingBehavior is unknown', () => { + const device = createDevice('UNKNOWN_BEHAVIOR', []); + + expect(resolveCallingBackend(device)).toBe(CALLING_BACKEND.INVALID); + }); + + it('returns INVALID when callingBehavior is NATIVE_WEBEX_TEAMS_CALLING but no matching entitlement', () => { + const device = createDevice('NATIVE_WEBEX_TEAMS_CALLING', ['some-other-entitlement']); + + expect(resolveCallingBackend(device)).toBe(CALLING_BACKEND.INVALID); + }); + + it('matches the first qualifying entitlement when multiple are present', () => { + const device = createDevice('NATIVE_WEBEX_TEAMS_CALLING', [ + 'broadworks-connector', + 'bc-sp-basic', + ]); + + expect(resolveCallingBackend(device)).toBe(CALLING_BACKEND.BWRKS); + }); +}); + +describe('getCallingBackEnd', () => { + it('passes webex.internal.device to resolveCallingBackend and returns its result', () => { + const device = { + url: 'https://wdm.example.com/devices/123', + userId: 'user-id', + orgId: 'org-id', + version: '1.0', + callingBehavior: 'NATIVE_SIP_CALL_TO_UCM', + features: { + developer: {models: [], get: jest.fn()}, + entitlement: {models: []}, + }, + }; + + const mockWebex = {internal: {device}} as any; + + const result = getCallingBackEnd(mockWebex); + + expect(result).toBe(resolveCallingBackend(device)); + expect(resolveCallingBackend(device)).toBe(CALLING_BACKEND.UCM); + expect(result).toBe(CALLING_BACKEND.UCM); + }); +}); diff --git a/packages/calling/src/common/Utils.ts b/packages/calling/src/common/Utils.ts index ecc05d5d492..801d2645e12 100644 --- a/packages/calling/src/common/Utils.ts +++ b/packages/calling/src/common/Utils.ts @@ -129,7 +129,7 @@ import { WEBEX_API_BTS, BW_XSI_ENDPOINT_VERSION_WITH_SLASH, } from './constants'; -import {Model, WebexSDK} from '../SDKConnector/types'; +import {Model, WDMDevice, WebexSDK} from '../SDKConnector/types'; import SDKConnector from '../SDKConnector'; import {CallSettingResponse} from '../CallSettings/types'; import {ContactResponse} from '../Contacts/types'; @@ -1216,16 +1216,16 @@ export const waitForMsecs = (msec: number) => }); /** - * Register calling backend. + * Determine the calling backend from the device object. * - * @param webex -. + * @param device - The device object containing callingBehavior and entitlement features. * @returns CallingBackEnd. */ -export function getCallingBackEnd(webex: WebexSDK): CALLING_BACKEND { - const entModels: Model[] = webex.internal.device.features.entitlement.models; +export function resolveCallingBackend(device: WDMDevice): CALLING_BACKEND { + const entModels: Model[] = device.features.entitlement.models; let callingBackend; - if (webex.internal.device.callingBehavior === NATIVE_WEBEX_TEAMS_CALLING) { + if (device.callingBehavior === NATIVE_WEBEX_TEAMS_CALLING) { for (let i = 0; i < entModels.length; i += 1) { if ( entModels[i][VALUES][KEY] === ENTITLEMENT_BASIC || @@ -1238,13 +1238,23 @@ export function getCallingBackEnd(webex: WebexSDK): CALLING_BACKEND { break; } } - } else if (webex.internal.device.callingBehavior === NATIVE_SIP_CALL_TO_UCM) { + } else if (device.callingBehavior === NATIVE_SIP_CALL_TO_UCM) { callingBackend = CALLING_BACKEND.UCM; } else { callingBackend = CALLING_BACKEND.INVALID; } - return callingBackend as CALLING_BACKEND; + return callingBackend || CALLING_BACKEND.INVALID; +} + +/** + * Register calling backend. + * + * @param webex -. + * @returns CallingBackEnd. + */ +export function getCallingBackEnd(webex: WebexSDK): CALLING_BACKEND { + return resolveCallingBackend(webex.internal.device); } /** diff --git a/packages/calling/src/index.ts b/packages/calling/src/index.ts index f450bca06c5..542eb4fb803 100644 --- a/packages/calling/src/index.ts +++ b/packages/calling/src/index.ts @@ -46,10 +46,13 @@ export { CallDetails, CallDirection, CallType, + CALLING_BACKEND, DisplayInformation, SORT, SORT_BY, } from './common/types'; +export {resolveCallingBackend} from './common/Utils'; +export {WDMDevice} from './SDKConnector/types'; export {CallError, LineError} from './Errors'; export {ICall, TransferType} from './CallingClient/calling/types'; export {LOGGER} from './Logger/types';