diff --git a/docs/samples/contact-center/app.js b/docs/samples/contact-center/app.js index 5776f561046..e2aa257225e 100644 --- a/docs/samples/contact-center/app.js +++ b/docs/samples/contact-center/app.js @@ -148,11 +148,41 @@ async function handleAgentLogin(e) { } } +async function fetchBuddyAgents() { + try { + const buddyAgentsResponse = await webex.cc.getBuddyAgents({channelName: 'telephony', state: 'Available'}); + const buddyAgentsDropdown = document.getElementById('buddyAgentsDropdown'); + buddyAgentsDropdown.innerHTML = ''; // Clear previous options + + if (buddyAgentsResponse.agentList.length === 0) { + const option = document.createElement('option'); + option.text = 'No buddy agents available'; + option.disabled = true; + buddyAgentsDropdown.add(option); + return; + } + + buddyAgentsResponse.agentList.forEach((agent) => { + const option = document.createElement('option'); + option.text = `${agent.agentName} - ${agent.state}`; + option.value = agent.agentId; + buddyAgentsDropdown.add(option); + }); + } catch (error) { + const buddyAgentsDropdown = document.getElementById('buddyAgentsDropdown'); + buddyAgentsDropdown.innerHTML = ''; // Clear previous options + const option = document.createElement('option'); + option.text = `Failed to fetch buddy agents, ${error}`; + option.disabled = true; + buddyAgentsDropdown.add(option); + console.log('Failed to fetch buddy agents', error); + } +} + function doAgentLogin() { webex.cc.stationLogin({teamId: teamsDropdown.value, loginOption: agentDeviceType, dialNumber: dialNumber.value}).then((response) => { console.log('Agent Logged in successfully', response); - } - ).catch((error) => { + }).catch((error) => { console.log('Agent Login failed', error); }); } diff --git a/docs/samples/contact-center/index.html b/docs/samples/contact-center/index.html index f6a2cdbf6e1..34f76e83882 100644 --- a/docs/samples/contact-center/index.html +++ b/docs/samples/contact-center/index.html @@ -80,7 +80,7 @@

- + @@ -91,7 +91,7 @@

Agent Desktop Using Webex CC SDK

- +
Agent @@ -121,6 +121,11 @@

+
+ Buddy Agents + + +
@@ -132,4 +137,4 @@

- \ No newline at end of file + diff --git a/packages/@webex/plugin-cc/src/cc.ts b/packages/@webex/plugin-cc/src/cc.ts index b3f052f202b..a75153ed756 100644 --- a/packages/@webex/plugin-cc/src/cc.ts +++ b/packages/@webex/plugin-cc/src/cc.ts @@ -9,12 +9,14 @@ import { SubscribeRequest, WelcomeEvent, STATION_LOGIN_TYPE, + GetBuddyAgentsRequest, + CHANNEL_NAME, } from './types'; +import {BuddyAgentsSuccess, StationLoginSuccess} from './services/types'; import {READY, CC_FILE} from './constants'; import Agent from './features/Agent'; import HttpRequest from './services/HttpRequest'; import WebRTCCalling from './WebRTCCalling'; -import {StationLoginSuccess} from './services/types'; export default class ContactCenter extends WebexPlugin implements IContactCenter { namespace = 'cc'; @@ -136,4 +138,36 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter return Promise.reject(error); } } + + /** + * @param options - GetBuddyAgentsRequest + * @returns Promise + * @throws Error + * @example + * const buddyAgents = await webex.cc.getBuddyAgents({ + * channelType: CHANNEL_TYPE.TELEPHONY, + * state: BuddyAgentState.AVAILABLE, + * }); + */ + public async getBuddyAgents( + options: GetBuddyAgentsRequest = { + channelName: CHANNEL_NAME.TELEPHONY, + } + ): Promise { + try { + const {channelName, state} = options; + const buddyAgents = await this.agent.getBuddyAgents({ + agentProfileId: this.agentConfig.agentProfileId, + channelName, + state, + }); + this.$webex.logger.log('Get Buddy Agents API SUCCESS'); + + return buddyAgents; + } catch (error) { + this.$webex.logger.error('Get Buddy Agents API FAILED'); + + return Promise.reject(new Error('Error while retrieving buddy agents', error.message)); + } + } } diff --git a/packages/@webex/plugin-cc/src/features/Agent.ts b/packages/@webex/plugin-cc/src/features/Agent.ts index 14f7ed8afcc..6a8a33d1989 100644 --- a/packages/@webex/plugin-cc/src/features/Agent.ts +++ b/packages/@webex/plugin-cc/src/features/Agent.ts @@ -1,7 +1,7 @@ import {STATION_LOGIN_TYPE, WebexSDK} from '../types'; import HttpRequest from '../services/HttpRequest'; import AgentService from '../services/AgentService'; -import {StationLoginSuccess} from '../services/types'; +import {BuddyAgents, BuddyAgentsSuccess, StationLoginSuccess} from '../services/types'; export default class Agent { private webex: WebexSDK; @@ -34,4 +34,15 @@ export default class Agent { return Promise.reject(new Error('Error while performing agent login', error)); } } + + public async getBuddyAgents(options: BuddyAgents): Promise { + try { + const buddyAgentsResponse = await this.agentService.getBuddyAgents(options); + this.webex.logger.log('Buddy Agents API SUCCESS'); + + return buddyAgentsResponse; + } catch (error) { + return Promise.reject(new Error('Error while retrieving buddy agents', error)); + } + } } diff --git a/packages/@webex/plugin-cc/src/services/AgentService.ts b/packages/@webex/plugin-cc/src/services/AgentService.ts index 8eeb8f5bbbd..435f421eb5d 100644 --- a/packages/@webex/plugin-cc/src/services/AgentService.ts +++ b/packages/@webex/plugin-cc/src/services/AgentService.ts @@ -1,7 +1,15 @@ import {STATION_LOGIN_TYPE, WebexSDK, HTTP_METHODS} from '../types'; -import {AGENT, LOGIN_API, WCC_API_GATEWAY, WEB_RTC_PREFIX} from './constants'; +import { + AGENT, + GET_BUDDY_AGENTS_API, + BuddyAgentsEvent, + BuddyAgentsRetrieveFailedEvent, + LOGIN_API, + WCC_API_GATEWAY, + WEB_RTC_PREFIX, +} from './constants'; import HttpRequest from './HttpRequest'; -import {StationLoginSuccess} from './types'; +import {BuddyAgentsSuccess, BuddyAgents, StationLoginSuccess} from './types'; export default class AgentService { private webex: WebexSDK; @@ -56,4 +64,28 @@ export default class AgentService { return Promise.reject(error); } } + + public async getBuddyAgents(options: BuddyAgents): Promise { + try { + const data = await this.httpRequest.sendRequestWithEvent({ + service: WCC_API_GATEWAY, + resource: GET_BUDDY_AGENTS_API, + method: HTTP_METHODS.POST, + payload: { + agentProfileId: options.agentProfileId, + mediaType: options.channelName, + state: options.state || undefined, + }, + eventType: BuddyAgentsEvent, + success: [BuddyAgentsEvent], + failure: [BuddyAgentsRetrieveFailedEvent], + }); + + return data as BuddyAgentsSuccess; + } catch (error) { + this.webex.logger.error(`Error during get buddy agents: ${error}`); + + return Promise.reject(new Error('Error while retrieving buddy agents', error)); + } + } } diff --git a/packages/@webex/plugin-cc/src/services/HttpRequest.ts b/packages/@webex/plugin-cc/src/services/HttpRequest.ts index e3c03cac7fb..d6e9c82f187 100644 --- a/packages/@webex/plugin-cc/src/services/HttpRequest.ts +++ b/packages/@webex/plugin-cc/src/services/HttpRequest.ts @@ -87,6 +87,9 @@ class HttpRequest { try { const {service, resource, method, payload, eventType, success, failure} = options; + // prune undefined values + Object.keys(payload).forEach((key) => payload[key] === undefined && delete payload[key]); + // Send the service request const response = await this.webex.request({ service, @@ -94,7 +97,14 @@ class HttpRequest { method, body: payload, }); - this.webex.logger.log(`Service request sent successfully: ${response}`); + // @ts-ignore + const trackingid = response.headers.trackingid; + + this.webex.logger.log( + `Service request sent successfully to ${service}/${resource} with payload: ${JSON.stringify( + payload + )}, Request TrackingID: ${trackingid}` + ); // Listen for the event return new Promise((resolve, reject) => { diff --git a/packages/@webex/plugin-cc/src/services/constants.ts b/packages/@webex/plugin-cc/src/services/constants.ts index 24198ef0e33..b0965298f4e 100644 --- a/packages/@webex/plugin-cc/src/services/constants.ts +++ b/packages/@webex/plugin-cc/src/services/constants.ts @@ -6,4 +6,10 @@ export const AGENT = 'agent'; // CC GATEWAY API URL PATHS export const SUBSCRIBE_API = 'v1/notification/subscribe'; export const LOGIN_API = 'v1/agents/login'; +export const GET_BUDDY_AGENTS_API = 'v1/agents/buddyList'; export const WEB_RTC_PREFIX = 'webrtc-'; + +export const AgentDesktopMessage = 'AgentDesktopMessage'; + +export const BuddyAgentsEvent = 'BuddyAgents'; +export const BuddyAgentsRetrieveFailedEvent = 'BuddyAgentsRetrieveFailed'; diff --git a/packages/@webex/plugin-cc/src/services/types.ts b/packages/@webex/plugin-cc/src/services/types.ts index 112b0ab65b6..00289d0484c 100644 --- a/packages/@webex/plugin-cc/src/services/types.ts +++ b/packages/@webex/plugin-cc/src/services/types.ts @@ -1,3 +1,6 @@ +import {CHANNEL_NAME, BUDDY_AGENT_STATE} from '../types'; +import {BuddyAgentsEvent, AgentDesktopMessage} from './constants'; + type Enum> = T[keyof T]; export type Msg = { @@ -228,6 +231,31 @@ export interface StationLoginSuccess { type: 'AgentStationLoginSuccess'; } +export type BuddyAgents = { + agentProfileId: string; + channelName: CHANNEL_NAME; + state?: BUDDY_AGENT_STATE; +}; + +export interface BuddyDetails { + agentId: string; + state: string; + teamId: string; + dn: string; + agentName: string; + siteId: string; +} + +export type BuddyAgentsSuccess = { + eventType: typeof AgentDesktopMessage; + agentId: string; + orgId: string; + trackingId: string; + agentList: BuddyDetails[]; + agentSessionId: string; + type: typeof BuddyAgentsEvent; +}; + export type SubscribeResponse = { statusCode: number; body: { diff --git a/packages/@webex/plugin-cc/src/types.ts b/packages/@webex/plugin-cc/src/types.ts index 4fb81ee7256..623df986faa 100644 --- a/packages/@webex/plugin-cc/src/types.ts +++ b/packages/@webex/plugin-cc/src/types.ts @@ -148,3 +148,24 @@ export interface SubscribeRequest { } export type EventResult = IAgentProfile; + +export const CHANNEL_NAME = { + TELEPHONY: 'telephony', + CHAT: 'chat', + SOCIAL: 'social', + EMAIL: 'email', +} as const; + +export type CHANNEL_NAME = Enum; + +export const BUDDY_AGENT_STATE = { + AVAILABLE: 'Available', + IDLE: 'Idle', +} as const; + +export type BUDDY_AGENT_STATE = Enum; + +export type GetBuddyAgentsRequest = { + channelName: CHANNEL_NAME; + state?: BUDDY_AGENT_STATE; +}; diff --git a/packages/@webex/plugin-cc/test/unit/spec/features/Agent.ts b/packages/@webex/plugin-cc/test/unit/spec/features/Agent.ts index ba35a86774f..e98d2115473 100644 --- a/packages/@webex/plugin-cc/test/unit/spec/features/Agent.ts +++ b/packages/@webex/plugin-cc/test/unit/spec/features/Agent.ts @@ -1,8 +1,8 @@ -import { STATION_LOGIN_TYPE, WebexSDK } from '../../../../src/types'; +import {STATION_LOGIN_TYPE, WebexSDK, CHANNEL_NAME, BUDDY_AGENT_STATE} from '../../../../src/types'; import HttpRequest from '../../../../src/services/HttpRequest'; import AgentService from '../../../../src/services/AgentService'; import Agent from '../../../../src/features/Agent'; -import { StationLoginSuccess } from '../../../../src/services/types'; +import {BuddyAgents, BuddyAgentsSuccess, StationLoginSuccess} from '../../../../src/services/types'; // Mock dependencies jest.mock('../../../../src/services/AgentService'); @@ -85,4 +85,49 @@ describe('Agent', () => { expect(webexMock.logger.log).toHaveBeenCalledWith('LOGIN API SUCCESS'); expect(response).toBe(loginResponse); }); -}); \ No newline at end of file + + describe('getBuddyAgents', () => { + const options: BuddyAgents = { + channelName: CHANNEL_NAME.TELEPHONY, + state: BUDDY_AGENT_STATE.AVAILABLE, + agentProfileId: '123', + }; + + it('should return buddy agents on success', async () => { + const buddyAgentsSuccess: BuddyAgentsSuccess = { + agentId: 'agentId1', + eventType: 'AgentDesktopMessage', + agentList: [ + { + agentId: '12345', + state: 'Available', + teamId: 'abcd', + dn: '1001', + agentName: 'Agent1 Name1', + siteId: 'site1', + }, + ], + orgId: 'org1', + trackingId: 'track1', + agentSessionId: 'session1', + type: 'BuddyAgents', + }; + agentServiceMock.getBuddyAgents.mockResolvedValue(buddyAgentsSuccess); + + const result = await agent.getBuddyAgents(options); + + expect(result).toEqual(buddyAgentsSuccess); + expect(agentServiceMock.getBuddyAgents).toHaveBeenCalledWith(options); + }); + + it('should throw an error when getBuddyAgents fails', async () => { + const error = new Error('API Error'); + agentServiceMock.getBuddyAgents.mockRejectedValue(error); + + await expect(agent.getBuddyAgents(options)).rejects.toThrow( + 'Error while retrieving buddy agents' + ); + expect(agentServiceMock.getBuddyAgents).toHaveBeenCalledWith(options); + }); + }); +});