@webex/contact-center 3.12.0-next.147 → 3.12.0-next.149

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.
Files changed (82) hide show
  1. package/.sdd/manifest.json +4 -2
  2. package/ai-docs/CONTRACTS.md +2 -0
  3. package/dist/cc.js +3 -1
  4. package/dist/cc.js.map +1 -1
  5. package/dist/constants.js +10 -1
  6. package/dist/constants.js.map +1 -1
  7. package/dist/index.js +9 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/metrics/constants.js +9 -0
  10. package/dist/metrics/constants.js.map +1 -1
  11. package/dist/services/ApiAiAssistant.js +87 -10
  12. package/dist/services/ApiAiAssistant.js.map +1 -1
  13. package/dist/services/config/types.js +10 -4
  14. package/dist/services/config/types.js.map +1 -1
  15. package/dist/services/core/types.js.map +1 -1
  16. package/dist/services/task/Task.js +202 -1
  17. package/dist/services/task/Task.js.map +1 -1
  18. package/dist/services/task/TaskFactory.js +4 -4
  19. package/dist/services/task/TaskFactory.js.map +1 -1
  20. package/dist/services/task/TaskManager.js +113 -26
  21. package/dist/services/task/TaskManager.js.map +1 -1
  22. package/dist/services/task/TaskUtils.js +11 -1
  23. package/dist/services/task/TaskUtils.js.map +1 -1
  24. package/dist/services/task/constants.js +11 -2
  25. package/dist/services/task/constants.js.map +1 -1
  26. package/dist/services/task/digital/Digital.js +2 -2
  27. package/dist/services/task/digital/Digital.js.map +1 -1
  28. package/dist/services/task/types.js +31 -0
  29. package/dist/services/task/types.js.map +1 -1
  30. package/dist/services/task/voice/Voice.js +2 -2
  31. package/dist/services/task/voice/Voice.js.map +1 -1
  32. package/dist/services/task/voice/WebRTC.js +2 -2
  33. package/dist/services/task/voice/WebRTC.js.map +1 -1
  34. package/dist/types/constants.d.ts +9 -0
  35. package/dist/types/index.d.ts +4 -5
  36. package/dist/types/metrics/constants.d.ts +8 -0
  37. package/dist/types/services/ApiAiAssistant.d.ts +4 -2
  38. package/dist/types/services/config/types.d.ts +17 -7
  39. package/dist/types/services/task/Task.d.ts +15 -4
  40. package/dist/types/services/task/TaskFactory.d.ts +3 -2
  41. package/dist/types/services/task/TaskUtils.d.ts +6 -0
  42. package/dist/types/services/task/constants.d.ts +9 -0
  43. package/dist/types/services/task/digital/Digital.d.ts +3 -2
  44. package/dist/types/services/task/types.d.ts +109 -1
  45. package/dist/types/services/task/voice/Voice.d.ts +3 -2
  46. package/dist/types/services/task/voice/WebRTC.d.ts +3 -2
  47. package/dist/types/types.d.ts +10 -4
  48. package/dist/types.js +8 -4
  49. package/dist/types.js.map +1 -1
  50. package/dist/webex.js +1 -1
  51. package/package.json +3 -3
  52. package/src/cc.ts +4 -0
  53. package/src/constants.ts +12 -0
  54. package/src/index.ts +10 -2
  55. package/src/metrics/ai-docs/metrics-spec.md +12 -4
  56. package/src/metrics/constants.ts +10 -0
  57. package/src/services/ApiAiAssistant.ts +120 -12
  58. package/src/services/config/types.ts +10 -4
  59. package/src/services/core/types.ts +31 -1
  60. package/src/services/task/Task.ts +308 -5
  61. package/src/services/task/TaskFactory.ts +26 -5
  62. package/src/services/task/TaskManager.ts +148 -29
  63. package/src/services/task/TaskUtils.ts +13 -0
  64. package/src/services/task/ai-docs/task-spec.md +30 -5
  65. package/src/services/task/constants.ts +10 -0
  66. package/src/services/task/digital/Digital.ts +8 -3
  67. package/src/services/task/types.ts +139 -0
  68. package/src/services/task/voice/Voice.ts +8 -3
  69. package/src/services/task/voice/WebRTC.ts +6 -3
  70. package/src/types.ts +10 -4
  71. package/test/unit/fixtures/aiSummaryTestUtils.ts +40 -0
  72. package/test/unit/spec/cc.ts +1314 -32
  73. package/test/unit/spec/index.ts +881 -0
  74. package/test/unit/spec/services/ApiAiAssistant.ts +122 -6
  75. package/test/unit/spec/services/core/WebexRequest.ts +5 -6
  76. package/test/unit/spec/services/task/Task.ts +1988 -10
  77. package/test/unit/spec/services/task/TaskFactory.ts +17 -0
  78. package/test/unit/spec/services/task/TaskManager.ts +2327 -180
  79. package/test/unit/spec/services/task/TaskUtils.ts +72 -0
  80. package/test/unit/spec/services/task/voice/Voice.ts +353 -18
  81. package/umd/contact-center.min.js +2 -2
  82. package/umd/contact-center.min.js.map +1 -1
@@ -9,13 +9,51 @@ import {
9
9
  WebexSDK,
10
10
  } from '../../../../src/types';
11
11
 
12
- jest.mock('../../../../src/metrics/MetricsManager');
13
- jest.mock('../../../../src/logger-proxy');
12
+ jest.mock('../../../../src/metrics/MetricsManager', () => ({
13
+ __esModule: true,
14
+ default: {
15
+ getInstance: jest.fn(),
16
+ },
17
+ }));
18
+ jest.mock('../../../../src/logger-proxy', () => ({
19
+ __esModule: true,
20
+ default: {
21
+ log: jest.fn(),
22
+ error: jest.fn(),
23
+ info: jest.fn(),
24
+ warn: jest.fn(),
25
+ trace: jest.fn(),
26
+ debug: jest.fn(),
27
+ },
28
+ }));
29
+ jest.mock('../../../../src/services/core/WebexRequest', () => ({
30
+ __esModule: true,
31
+ default: {
32
+ getInstance: jest.fn(),
33
+ },
34
+ }));
35
+ jest.mock('../../../../src/services/core/Utils', () => ({
36
+ __esModule: true,
37
+ getErrorDetails: jest.fn((error: any, methodName: string, moduleName: string) => {
38
+ const LoggerProxyMock = require('../../../../src/logger-proxy').default;
39
+ const reason = error?.details?.data?.reason ?? `Error while performing ${methodName}`;
40
+ const detailedError = new Error(reason) as Error & {data?: Record<string, unknown>};
41
+
42
+ detailedError.data = {};
43
+ LoggerProxyMock.error(`${methodName} failed with reason: ${reason}`, {
44
+ module: moduleName,
45
+ method: methodName,
46
+ });
47
+
48
+ return {error: detailedError, reason};
49
+ }),
50
+ }));
14
51
 
15
52
  describe('ApiAIAssistant', () => {
16
53
  let apiAIAssistant: ApiAIAssistant;
17
54
  let mockWebex: WebexSDK;
18
55
  let mockMetricsManager: jest.Mocked<MetricsManager>;
56
+ let mockUploadLogs: jest.Mock;
19
57
  let wellnessContext: {
20
58
  isWellnessBreakEnabled: boolean;
21
59
  agentId?: string;
@@ -24,9 +62,7 @@ describe('ApiAIAssistant', () => {
24
62
 
25
63
  beforeEach(() => {
26
64
  jest.clearAllMocks();
27
- jest.spyOn(WebexRequest, 'getInstance').mockReturnValue({
28
- uploadLogs: jest.fn(),
29
- } as any);
65
+ mockUploadLogs = jest.fn();
30
66
 
31
67
  mockWebex = {
32
68
  credentials: {
@@ -47,6 +83,11 @@ describe('ApiAIAssistant', () => {
47
83
  once: jest.fn(),
48
84
  } as unknown as WebexSDK;
49
85
 
86
+ jest.spyOn(WebexRequest, 'getInstance').mockReturnValue({
87
+ request: mockWebex.request,
88
+ uploadLogs: mockUploadLogs,
89
+ } as any);
90
+
50
91
  mockMetricsManager = {
51
92
  trackEvent: jest.fn(),
52
93
  timeEvent: jest.fn(),
@@ -57,6 +98,79 @@ describe('ApiAIAssistant', () => {
57
98
  apiAIAssistant = new ApiAIAssistant(mockWebex, () => wellnessContext);
58
99
  });
59
100
 
101
+ it('sends a summary request and resolves it from the matching RTD event', async () => {
102
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: {accepted: true}});
103
+
104
+ const request = apiAIAssistant.requestAndWaitForRtd({
105
+ correlationId: 'conversation-1',
106
+ rtdEventType: 'POST_CALL_SUMMARY',
107
+ timeoutMs: 15000,
108
+ createTimeoutError: () => new Error('TIMEOUT'),
109
+ agentId: 'agent-1',
110
+ interactionId: 'interaction-1',
111
+ eventType: 'CTI_EVENT' as any,
112
+ eventName: 'GET_POST_CALL_SUMMARY' as any,
113
+ eventMetaData: {conversationId: 'conversation-1'},
114
+ timeout: 15000,
115
+ });
116
+
117
+ await Promise.resolve();
118
+ expect(apiAIAssistant.resolveFromRtdEvent('POST_CALL_SUMMARY', 'conversation-1', {summary: 'ready'})).toBe(
119
+ 'resolved'
120
+ );
121
+ await expect(request).resolves.toEqual({summary: 'ready'});
122
+ expect(mockWebex.request).toHaveBeenCalledTimes(1);
123
+ const requestBody = (mockWebex.request as jest.Mock).mock.calls[0][0].body;
124
+
125
+ expect(requestBody.publishTimestamp).toEqual(expect.any(Number));
126
+ expect(requestBody.eventDetails.data).toMatchObject({
127
+ conversationId: 'conversation-1',
128
+ interactionId: 'interaction-1',
129
+ actionTimeStamp: expect.any(String),
130
+ });
131
+ });
132
+
133
+ it('rejects an RTD request when its timeout expires', async () => {
134
+ jest.useFakeTimers();
135
+ (mockWebex.request as jest.Mock).mockResolvedValue({body: {accepted: true}});
136
+ const request = apiAIAssistant.requestAndWaitForRtd({
137
+ correlationId: 'conversation-1',
138
+ rtdEventType: 'POST_CALL_SUMMARY',
139
+ timeoutMs: 15000,
140
+ createTimeoutError: () => new Error('TIMEOUT'),
141
+ agentId: 'agent-1',
142
+ interactionId: 'interaction-1',
143
+ eventType: 'CTI_EVENT' as any,
144
+ eventName: 'GET_POST_CALL_SUMMARY' as any,
145
+ });
146
+
147
+ jest.advanceTimersByTime(15000);
148
+ await expect(request).rejects.toThrow('TIMEOUT');
149
+ jest.useRealTimers();
150
+ });
151
+
152
+ it('removes the pending RTD request when the HTTP request fails', async () => {
153
+ const error = new Error('HTTP_FAILED');
154
+ (mockWebex.request as jest.Mock).mockRejectedValue(error);
155
+
156
+ const request = apiAIAssistant.requestAndWaitForRtd({
157
+ correlationId: 'conversation-1',
158
+ rtdEventType: 'POST_CALL_SUMMARY',
159
+ timeoutMs: 15000,
160
+ createTimeoutError: () => new Error('TIMEOUT'),
161
+ agentId: 'agent-1',
162
+ interactionId: 'interaction-1',
163
+ eventType: 'CTI_EVENT' as any,
164
+ eventName: 'GET_POST_CALL_SUMMARY' as any,
165
+ });
166
+
167
+ await expect(request).rejects.toBeInstanceOf(Error);
168
+ expect(apiAIAssistant.resolveFromRtdEvent('POST_CALL_SUMMARY', 'conversation-1', {})).toBe(
169
+ 'not-found'
170
+ );
171
+ });
172
+
173
+
60
174
  it('should send transcript start event successfully', async () => {
61
175
  (mockWebex.request as jest.Mock).mockResolvedValue({body: {ok: true}});
62
176
 
@@ -65,7 +179,7 @@ describe('ApiAIAssistant', () => {
65
179
  'interaction-1',
66
180
  'CUSTOM_EVENT',
67
181
  'GET_TRANSCRIPTS',
68
- {action: 'START'}
182
+ {action: 'START', actionTimeStamp: 'caller-supplied'}
69
183
  );
70
184
 
71
185
  expect(mockWebex.request).toHaveBeenCalledTimes(1);
@@ -80,6 +194,8 @@ describe('ApiAIAssistant', () => {
80
194
  expect(requestArgs.body.eventName).toBe('GET_TRANSCRIPTS');
81
195
  expect(requestArgs.body.eventDetails.data.interactionId).toBe('interaction-1');
82
196
  expect(requestArgs.body.eventDetails.data.action).toBe('START');
197
+ expect(requestArgs.body.eventDetails.data.actionTimeStamp).toEqual(expect.any(String));
198
+ expect(requestArgs.body.eventDetails.data.actionTimeStamp).not.toBe('caller-supplied');
83
199
  expect(result).toEqual({ok: true});
84
200
  });
85
201
 
@@ -91,9 +91,8 @@ describe('WebexRequest', () => {
91
91
  trackEvent: jest.fn(),
92
92
  timeEvent: jest.fn(),
93
93
  };
94
-
94
+
95
95
  jest.spyOn(MetricsManager, 'getInstance').mockReturnValue(mockMetricsManager);
96
-
97
96
  });
98
97
 
99
98
  it('should upload logs and return the response', async () => {
@@ -114,8 +113,8 @@ describe('WebexRequest', () => {
114
113
  {module: WEBEX_REQUEST_FILE, method: 'uploadLogs'}
115
114
  );
116
115
  expect(mockMetricsManager.trackEvent).toBeCalledWith(
117
- "Upload Logs Success",
118
- {feedbackId: "mocked-uuid-12345", trackingId: '1234'},
116
+ "Upload Logs Success",
117
+ {feedbackId: "mocked-uuid-12345", trackingId: '1234'},
119
118
  ["behavioral"]
120
119
  );
121
120
  expect(mockWebex.internal.support.submitLogs).toHaveBeenCalledWith({... mockMetaData, feedbackId: "mocked-uuid-12345"}, undefined, {type: 'diff'});
@@ -137,8 +136,8 @@ describe('WebexRequest', () => {
137
136
  {module: WEBEX_REQUEST_FILE, method: 'uploadLogs'}
138
137
  );
139
138
  expect(mockMetricsManager.trackEvent).toBeCalledWith(
140
- "Upload Logs Failed",
141
- {stack: "My stack", feedbackId: "mocked-uuid-12345", correlationId: 'correlation-id'},
139
+ "Upload Logs Failed",
140
+ {stack: "My stack", feedbackId: "mocked-uuid-12345", correlationId: 'correlation-id'},
142
141
  ["behavioral"]
143
142
  );
144
143
  });