@webex/contact-center 3.12.0-next.66 → 3.12.0-next.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cc.js +51 -2
- package/dist/cc.js.map +1 -1
- package/dist/services/task/TaskManager.js +4 -0
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/types.js +12 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +17 -0
- package/dist/types/services/task/types.d.ts +13 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +74 -2
- package/src/services/task/TaskManager.ts +4 -0
- package/src/services/task/types.ts +14 -0
- package/src/types.ts +2 -0
- package/test/unit/spec/cc.ts +56 -0
- package/test/unit/spec/services/task/TaskManager.ts +14 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/src/cc.ts
CHANGED
|
@@ -357,6 +357,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
357
357
|
this.$webex.once(READY, () => {
|
|
358
358
|
// @ts-ignore
|
|
359
359
|
this.$config = this.config;
|
|
360
|
+
this.validatePluginConfig();
|
|
360
361
|
|
|
361
362
|
/**
|
|
362
363
|
* This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
|
|
@@ -430,6 +431,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
430
431
|
this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
431
432
|
};
|
|
432
433
|
|
|
434
|
+
/**
|
|
435
|
+
* Handles multi-login hydrate events emitted when browser login accepts an incoming call.
|
|
436
|
+
* @private
|
|
437
|
+
* @param {ITask} task The task object associated with multi-login hydrate
|
|
438
|
+
*/
|
|
439
|
+
private handleTaskMultiLoginHydrate = (task: ITask) => {
|
|
440
|
+
// @ts-ignore
|
|
441
|
+
this.trigger(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
442
|
+
};
|
|
443
|
+
|
|
433
444
|
private handleRTDWebsocketMessage = (payload: string) => {
|
|
434
445
|
this.taskManager.handleRealtimeWebsocketEvent(payload);
|
|
435
446
|
};
|
|
@@ -443,6 +454,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
443
454
|
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
444
455
|
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
445
456
|
this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
457
|
+
this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
446
458
|
this.taskManager.on(
|
|
447
459
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
448
460
|
this.handleCampaignPreviewReservation
|
|
@@ -576,6 +588,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
576
588
|
|
|
577
589
|
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
578
590
|
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
591
|
+
this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
579
592
|
this.taskManager.off(
|
|
580
593
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
581
594
|
this.handleCampaignPreviewReservation
|
|
@@ -724,6 +737,30 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
724
737
|
}
|
|
725
738
|
}
|
|
726
739
|
|
|
740
|
+
/**
|
|
741
|
+
* Checks whether WebRTC registration should be skipped by config.
|
|
742
|
+
* @returns {boolean}
|
|
743
|
+
* @private
|
|
744
|
+
*/
|
|
745
|
+
private isWebRTCRegistrationDisabled(): boolean {
|
|
746
|
+
return this.$config?.disableWebRTCRegistration === true;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Validates contact-center plugin configuration before service initialization
|
|
751
|
+
* @private
|
|
752
|
+
*/
|
|
753
|
+
private validatePluginConfig(): void {
|
|
754
|
+
if (
|
|
755
|
+
this.$config?.disableWebRTCRegistration === true &&
|
|
756
|
+
this.$config?.allowMultiLogin === false
|
|
757
|
+
) {
|
|
758
|
+
throw new Error(
|
|
759
|
+
'Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.'
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
727
764
|
/**
|
|
728
765
|
* Connects to the websocket and fetches the agent profile
|
|
729
766
|
* @returns {Promise<Profile>} Agent profile information
|
|
@@ -785,7 +822,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
785
822
|
|
|
786
823
|
if (
|
|
787
824
|
this.agentConfig.webRtcEnabled &&
|
|
788
|
-
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
825
|
+
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
|
|
826
|
+
!this.isWebRTCRegistrationDisabled()
|
|
789
827
|
) {
|
|
790
828
|
try {
|
|
791
829
|
await this.$webex.internal.mercury.connect();
|
|
@@ -799,6 +837,14 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
799
837
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
800
838
|
});
|
|
801
839
|
}
|
|
840
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
841
|
+
LoggerProxy.info(
|
|
842
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
843
|
+
{
|
|
844
|
+
module: CC_FILE,
|
|
845
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
846
|
+
}
|
|
847
|
+
);
|
|
802
848
|
}
|
|
803
849
|
|
|
804
850
|
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
@@ -899,8 +945,24 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
899
945
|
},
|
|
900
946
|
});
|
|
901
947
|
|
|
902
|
-
if (
|
|
948
|
+
if (
|
|
949
|
+
this.agentConfig.webRtcEnabled &&
|
|
950
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
951
|
+
!this.isWebRTCRegistrationDisabled()
|
|
952
|
+
) {
|
|
903
953
|
await this.webCallingService.registerWebCallingLine();
|
|
954
|
+
} else if (
|
|
955
|
+
this.agentConfig.webRtcEnabled &&
|
|
956
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
957
|
+
this.isWebRTCRegistrationDisabled()
|
|
958
|
+
) {
|
|
959
|
+
LoggerProxy.info(
|
|
960
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
961
|
+
{
|
|
962
|
+
module: CC_FILE,
|
|
963
|
+
method: METHODS.STATION_LOGIN,
|
|
964
|
+
}
|
|
965
|
+
);
|
|
904
966
|
}
|
|
905
967
|
|
|
906
968
|
const resp = await loginResponse;
|
|
@@ -1405,6 +1467,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1405
1467
|
this.agentConfig.deviceType = deviceType;
|
|
1406
1468
|
switch (deviceType) {
|
|
1407
1469
|
case LoginOption.BROWSER:
|
|
1470
|
+
if (this.isWebRTCRegistrationDisabled()) {
|
|
1471
|
+
LoggerProxy.info(
|
|
1472
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
1473
|
+
{
|
|
1474
|
+
module: CC_FILE,
|
|
1475
|
+
method: METHODS.HANDLE_DEVICE_TYPE,
|
|
1476
|
+
}
|
|
1477
|
+
);
|
|
1478
|
+
break;
|
|
1479
|
+
}
|
|
1408
1480
|
try {
|
|
1409
1481
|
await this.webCallingService.registerWebCallingLine();
|
|
1410
1482
|
} catch (error) {
|
|
@@ -301,6 +301,8 @@ export default class TaskManager extends EventEmitter {
|
|
|
301
301
|
task = this.updateTaskData(task, payload.data);
|
|
302
302
|
task.emit(TASK_EVENTS.TASK_ASSIGNED, task);
|
|
303
303
|
}
|
|
304
|
+
// We emit the event to the task manager to send the task data to the instance without mobius registration in case of multi login with desktop login
|
|
305
|
+
this.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
304
306
|
break;
|
|
305
307
|
case CC_EVENTS.AGENT_CONTACT_UNASSIGNED:
|
|
306
308
|
task = this.updateTaskData(task, {
|
|
@@ -506,6 +508,8 @@ export default class TaskManager extends EventEmitter {
|
|
|
506
508
|
// Fire only if you are the agent who initiated the consult
|
|
507
509
|
task.emit(TASK_EVENTS.TASK_CONSULTING, task);
|
|
508
510
|
}
|
|
511
|
+
// Also hydrate multi-login listeners when consulting state is received.
|
|
512
|
+
this.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
509
513
|
break;
|
|
510
514
|
case CC_EVENTS.AGENT_CONSULT_FAILED:
|
|
511
515
|
// This can only be received by the agent who initiated the consult.
|
|
@@ -602,6 +602,20 @@ export enum TASK_EVENTS {
|
|
|
602
602
|
* ```
|
|
603
603
|
*/
|
|
604
604
|
TASK_CAMPAIGN_CONTACT_UPDATED = 'task:campaignContactUpdated',
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Triggered when a we get accept an incoming web call
|
|
608
|
+
* This is used to send task data to the instance without mobius registration in case of multi login
|
|
609
|
+
* @example
|
|
610
|
+
* ```typescript
|
|
611
|
+
* task.on(TASK_EVENTS.TASK_MULIT_LOGIN_HYDRATE, (task: ITask) => {
|
|
612
|
+
* console.log('Mulit login detected:', task.data.interactionId);
|
|
613
|
+
* // Handle mulit login
|
|
614
|
+
* });
|
|
615
|
+
* ```
|
|
616
|
+
*/
|
|
617
|
+
|
|
618
|
+
TASK_MULTI_LOGIN_HYDRATE = 'task:multiLoginHydrate',
|
|
605
619
|
}
|
|
606
620
|
|
|
607
621
|
/**
|
package/src/types.ts
CHANGED
|
@@ -156,6 +156,8 @@ export interface CCPluginConfig {
|
|
|
156
156
|
};
|
|
157
157
|
/** Configuration for the calling client */
|
|
158
158
|
callingClientConfig: CallingClientConfig;
|
|
159
|
+
/** Whether to skip Mobius/WebRTC registration for browser login flows */
|
|
160
|
+
disableWebRTCRegistration?: boolean;
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
/**
|
package/test/unit/spec/cc.ts
CHANGED
|
@@ -202,6 +202,35 @@ describe('webex.cc', () => {
|
|
|
202
202
|
webex.emit('ready');
|
|
203
203
|
});
|
|
204
204
|
|
|
205
|
+
it('should throw when WebRTC registration is disabled without multi-login', () => {
|
|
206
|
+
const invalidWebex = MockWebex({
|
|
207
|
+
children: {
|
|
208
|
+
mercury: Mercury,
|
|
209
|
+
},
|
|
210
|
+
logger: {
|
|
211
|
+
log: jest.fn(),
|
|
212
|
+
error: jest.fn(),
|
|
213
|
+
info: jest.fn(),
|
|
214
|
+
},
|
|
215
|
+
credentials: {
|
|
216
|
+
getOrgId: jest.fn(() => 'mockOrgId'),
|
|
217
|
+
},
|
|
218
|
+
config: {
|
|
219
|
+
...config,
|
|
220
|
+
cc: {
|
|
221
|
+
...config.cc,
|
|
222
|
+
allowMultiLogin: false,
|
|
223
|
+
disableWebRTCRegistration: true,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
once: jest.fn((event, callback) => callback()),
|
|
227
|
+
}) as unknown as WebexSDK;
|
|
228
|
+
|
|
229
|
+
expect(() => new ContactCenter({parent: invalidWebex})).toThrow(
|
|
230
|
+
'Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.'
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
205
234
|
describe('cc.getDeviceId', () => {
|
|
206
235
|
it('should return dialNumber when loginOption is EXTENSION', () => {
|
|
207
236
|
const loginOption = LoginOption.EXTENSION;
|
|
@@ -336,6 +365,10 @@ describe('webex.cc', () => {
|
|
|
336
365
|
TASK_EVENTS.TASK_HYDRATE,
|
|
337
366
|
expect.any(Function)
|
|
338
367
|
);
|
|
368
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
369
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
370
|
+
expect.any(Function)
|
|
371
|
+
);
|
|
339
372
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
340
373
|
expect(webex.cc.services.rtdWebSocketManager.initWebSocket).toHaveBeenCalledWith({
|
|
341
374
|
body: {
|
|
@@ -484,6 +517,10 @@ describe('webex.cc', () => {
|
|
|
484
517
|
TASK_EVENTS.TASK_HYDRATE,
|
|
485
518
|
expect.any(Function)
|
|
486
519
|
);
|
|
520
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
521
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
522
|
+
expect.any(Function)
|
|
523
|
+
);
|
|
487
524
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
488
525
|
|
|
489
526
|
expect(configSpy).toHaveBeenCalled();
|
|
@@ -1586,6 +1623,10 @@ describe('webex.cc', () => {
|
|
|
1586
1623
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1587
1624
|
expect.any(Function)
|
|
1588
1625
|
);
|
|
1626
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1627
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1628
|
+
expect.any(Function)
|
|
1629
|
+
);
|
|
1589
1630
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1590
1631
|
expect(webex.cc.services.rtdWebSocketManager.off).toHaveBeenCalledWith(
|
|
1591
1632
|
'message',
|
|
@@ -1642,6 +1683,13 @@ describe('webex.cc', () => {
|
|
|
1642
1683
|
const [, hydrateCallback] = hydrateCalls[0];
|
|
1643
1684
|
expect(hydrateCallback).toBe(webex.cc['handleTaskHydrate']);
|
|
1644
1685
|
|
|
1686
|
+
const multiLoginHydrateCalls = mockTaskManager.off.mock.calls.filter(
|
|
1687
|
+
([evt]) => evt === TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE
|
|
1688
|
+
);
|
|
1689
|
+
expect(multiLoginHydrateCalls).toHaveLength(1);
|
|
1690
|
+
const [, multiLoginHydrateCallback] = multiLoginHydrateCalls[0];
|
|
1691
|
+
expect(multiLoginHydrateCallback).toBe(webex.cc['handleTaskMultiLoginHydrate']);
|
|
1692
|
+
|
|
1645
1693
|
const messageCalls = mockWebSocketManager.off.mock.calls.filter(([evt]) => evt === 'message');
|
|
1646
1694
|
expect(messageCalls).toHaveLength(1);
|
|
1647
1695
|
const [, messageCallback] = messageCalls[0];
|
|
@@ -1667,6 +1715,10 @@ describe('webex.cc', () => {
|
|
|
1667
1715
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1668
1716
|
expect.any(Function)
|
|
1669
1717
|
);
|
|
1718
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1719
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1720
|
+
expect.any(Function)
|
|
1721
|
+
);
|
|
1670
1722
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1671
1723
|
expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
|
|
1672
1724
|
'connectionLost',
|
|
@@ -1710,6 +1762,10 @@ describe('webex.cc', () => {
|
|
|
1710
1762
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1711
1763
|
expect.any(Function)
|
|
1712
1764
|
);
|
|
1765
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1766
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1767
|
+
expect.any(Function)
|
|
1768
|
+
);
|
|
1713
1769
|
|
|
1714
1770
|
expect(LoggerProxy.error).toHaveBeenCalledWith(`Error during deregister: ${mockError}`, {
|
|
1715
1771
|
module: CC_FILE,
|
|
@@ -319,6 +319,10 @@ describe('TaskManager', () => {
|
|
|
319
319
|
TASK_EVENTS.TASK_ASSIGNED,
|
|
320
320
|
taskManager.getTask(taskId)
|
|
321
321
|
);
|
|
322
|
+
expect(taskIncomingSpy).toHaveBeenCalledWith(
|
|
323
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
324
|
+
taskManager.getTask(taskId)
|
|
325
|
+
);
|
|
322
326
|
});
|
|
323
327
|
|
|
324
328
|
it('should handle WebSocket message for AGENT_CONTACT_RESERVED and emit task:incoming for extension case', () => {
|
|
@@ -1537,6 +1541,7 @@ describe('TaskManager', () => {
|
|
|
1537
1541
|
});
|
|
1538
1542
|
|
|
1539
1543
|
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
1544
|
+
const taskManagerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
1540
1545
|
webSocketManagerMock.emit('message', JSON.stringify(initialConsultingPayload));
|
|
1541
1546
|
webSocketManagerMock.emit('message', JSON.stringify(consultingPayload));
|
|
1542
1547
|
expect(taskManager.getTask(taskId).data.isConsulted).toBe(true);
|
|
@@ -1544,6 +1549,10 @@ describe('TaskManager', () => {
|
|
|
1544
1549
|
TASK_EVENTS.TASK_CONSULT_ACCEPTED,
|
|
1545
1550
|
taskManager.getTask(taskId)
|
|
1546
1551
|
);
|
|
1552
|
+
expect(taskManagerEmitSpy).toHaveBeenCalledWith(
|
|
1553
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1554
|
+
taskManager.getTask(taskId)
|
|
1555
|
+
);
|
|
1547
1556
|
});
|
|
1548
1557
|
|
|
1549
1558
|
it('should emit TASK_CONSULT_ENDED event on AGENT_CONSULT_ENDED event', () => {
|
|
@@ -1812,6 +1821,7 @@ describe('TaskManager', () => {
|
|
|
1812
1821
|
webSocketManagerMock.emit('message', JSON.stringify(initalPayload));
|
|
1813
1822
|
taskManager.getTask(taskId).data.isConsulted = false;
|
|
1814
1823
|
const taskEmitSpy = jest.spyOn(taskManager.getTask(taskId), 'emit');
|
|
1824
|
+
const taskManagerEmitSpy = jest.spyOn(taskManager, 'emit');
|
|
1815
1825
|
const consultingPayload = {
|
|
1816
1826
|
data: {
|
|
1817
1827
|
...initalPayload.data,
|
|
@@ -1825,6 +1835,10 @@ describe('TaskManager', () => {
|
|
|
1825
1835
|
TASK_EVENTS.TASK_CONSULTING,
|
|
1826
1836
|
taskManager.getTask(taskId)
|
|
1827
1837
|
);
|
|
1838
|
+
expect(taskManagerEmitSpy).toHaveBeenCalledWith(
|
|
1839
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
1840
|
+
taskManager.getTask(taskId)
|
|
1841
|
+
);
|
|
1828
1842
|
});
|
|
1829
1843
|
|
|
1830
1844
|
it('should emit TASK_END event on AGENT_CONTACT_UNASSIGNED', () => {
|