@webex/contact-center 3.12.0-task-refactor.11 → 3.12.0-task-refactor.13

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 (101) hide show
  1. package/dist/cc.js +297 -83
  2. package/dist/cc.js.map +1 -1
  3. package/dist/constants.js +3 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/metrics/behavioral-events.js +39 -0
  6. package/dist/metrics/behavioral-events.js.map +1 -1
  7. package/dist/metrics/constants.js +8 -1
  8. package/dist/metrics/constants.js.map +1 -1
  9. package/dist/services/ApiAiAssistant.js +3 -0
  10. package/dist/services/ApiAiAssistant.js.map +1 -1
  11. package/dist/services/config/Util.js +1 -1
  12. package/dist/services/config/Util.js.map +1 -1
  13. package/dist/services/config/types.js +10 -0
  14. package/dist/services/config/types.js.map +1 -1
  15. package/dist/services/constants.js +1 -0
  16. package/dist/services/constants.js.map +1 -1
  17. package/dist/services/core/Err.js.map +1 -1
  18. package/dist/services/core/Utils.js +57 -6
  19. package/dist/services/core/Utils.js.map +1 -1
  20. package/dist/services/core/aqm-reqs.js +92 -17
  21. package/dist/services/core/aqm-reqs.js.map +1 -1
  22. package/dist/services/core/websocket/WebSocketManager.js +24 -3
  23. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  24. package/dist/services/index.js +1 -0
  25. package/dist/services/index.js.map +1 -1
  26. package/dist/services/task/Task.js +6 -0
  27. package/dist/services/task/Task.js.map +1 -1
  28. package/dist/services/task/TaskManager.js +155 -6
  29. package/dist/services/task/TaskManager.js.map +1 -1
  30. package/dist/services/task/TaskUtils.js +39 -11
  31. package/dist/services/task/TaskUtils.js.map +1 -1
  32. package/dist/services/task/constants.js +7 -1
  33. package/dist/services/task/constants.js.map +1 -1
  34. package/dist/services/task/dialer.js +129 -0
  35. package/dist/services/task/dialer.js.map +1 -1
  36. package/dist/services/task/state-machine/TaskStateMachine.js +30 -3
  37. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  38. package/dist/services/task/state-machine/actions.js +4 -0
  39. package/dist/services/task/state-machine/actions.js.map +1 -1
  40. package/dist/services/task/state-machine/constants.js +3 -0
  41. package/dist/services/task/state-machine/constants.js.map +1 -1
  42. package/dist/services/task/state-machine/guards.js +23 -0
  43. package/dist/services/task/state-machine/guards.js.map +1 -1
  44. package/dist/services/task/state-machine/types.js.map +1 -1
  45. package/dist/services/task/types.js +59 -0
  46. package/dist/services/task/types.js.map +1 -1
  47. package/dist/types/cc.d.ts +76 -2
  48. package/dist/types/constants.d.ts +3 -0
  49. package/dist/types/metrics/constants.d.ts +6 -0
  50. package/dist/types/services/config/types.d.ts +22 -1
  51. package/dist/types/services/core/Err.d.ts +6 -0
  52. package/dist/types/services/core/Utils.d.ts +20 -2
  53. package/dist/types/services/core/aqm-reqs.d.ts +49 -0
  54. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  55. package/dist/types/services/core/websocket/connection-service.d.ts +1 -0
  56. package/dist/types/services/task/Task.d.ts +1 -0
  57. package/dist/types/services/task/TaskUtils.d.ts +13 -0
  58. package/dist/types/services/task/constants.d.ts +6 -0
  59. package/dist/types/services/task/dialer.d.ts +45 -0
  60. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +36 -8
  61. package/dist/types/services/task/state-machine/constants.d.ts +3 -0
  62. package/dist/types/services/task/state-machine/guards.d.ts +2 -0
  63. package/dist/types/services/task/state-machine/types.d.ts +11 -0
  64. package/dist/types/services/task/types.d.ts +75 -1
  65. package/dist/types.js.map +1 -1
  66. package/dist/webex.js +1 -1
  67. package/package.json +2 -2
  68. package/src/cc.ts +389 -111
  69. package/src/constants.ts +3 -0
  70. package/src/metrics/behavioral-events.ts +42 -0
  71. package/src/metrics/constants.ts +9 -1
  72. package/src/services/ApiAiAssistant.ts +3 -0
  73. package/src/services/config/Util.ts +1 -1
  74. package/src/services/config/types.ts +12 -1
  75. package/src/services/constants.ts +1 -0
  76. package/src/services/core/Err.ts +3 -0
  77. package/src/services/core/Utils.ts +68 -5
  78. package/src/services/core/aqm-reqs.ts +100 -22
  79. package/src/services/core/websocket/WebSocketManager.ts +23 -2
  80. package/src/services/index.ts +1 -0
  81. package/src/services/task/Task.ts +13 -0
  82. package/src/services/task/TaskManager.ts +171 -7
  83. package/src/services/task/TaskUtils.ts +33 -1
  84. package/src/services/task/constants.ts +6 -0
  85. package/src/services/task/dialer.ts +136 -1
  86. package/src/services/task/state-machine/TaskStateMachine.ts +37 -5
  87. package/src/services/task/state-machine/actions.ts +4 -0
  88. package/src/services/task/state-machine/constants.ts +3 -0
  89. package/src/services/task/state-machine/guards.ts +30 -0
  90. package/src/services/task/state-machine/types.ts +14 -1
  91. package/src/services/task/types.ts +80 -0
  92. package/src/types.ts +4 -1
  93. package/test/unit/spec/cc.ts +294 -32
  94. package/test/unit/spec/services/config/index.ts +30 -2
  95. package/test/unit/spec/services/core/Utils.ts +367 -1
  96. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +91 -1
  97. package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
  98. package/test/unit/spec/services/task/TaskManager.ts +4 -2
  99. package/test/unit/spec/services/task/dialer.ts +372 -96
  100. package/umd/contact-center.min.js +2 -2
  101. package/umd/contact-center.min.js.map +1 -1
@@ -463,6 +463,48 @@ const eventTaxonomyMap: Record<string, BehavioralEventTaxonomy> = {
463
463
  target: 'outdial_ani_ep_fetch',
464
464
  verb: 'fail',
465
465
  },
466
+
467
+ // Campaign Preview API Events
468
+ [METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS]: {
469
+ product,
470
+ agent: 'user',
471
+ target: 'campaign_preview_accept',
472
+ verb: 'complete',
473
+ },
474
+ [METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED]: {
475
+ product,
476
+ agent: 'user',
477
+ target: 'campaign_preview_accept',
478
+ verb: 'fail',
479
+ },
480
+
481
+ // Campaign Preview Skip API Events
482
+ [METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS]: {
483
+ product,
484
+ agent: 'user',
485
+ target: 'campaign_preview_skip',
486
+ verb: 'complete',
487
+ },
488
+ [METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED]: {
489
+ product,
490
+ agent: 'user',
491
+ target: 'campaign_preview_skip',
492
+ verb: 'fail',
493
+ },
494
+
495
+ // Campaign Preview Remove API Events
496
+ [METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS]: {
497
+ product,
498
+ agent: 'user',
499
+ target: 'campaign_preview_remove',
500
+ verb: 'complete',
501
+ },
502
+ [METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED]: {
503
+ product,
504
+ agent: 'user',
505
+ target: 'campaign_preview_remove',
506
+ verb: 'fail',
507
+ },
466
508
  };
467
509
 
468
510
  /**
@@ -163,7 +163,15 @@ export const METRIC_EVENT_NAMES = {
163
163
  OUTDIAL_ANI_EP_FETCH_SUCCESS: 'Outdial ANI Entries Fetch Success',
164
164
  OUTDIAL_ANI_EP_FETCH_FAILED: 'Outdial ANI Entries Fetch Failed',
165
165
 
166
- // AI Assistant events
166
+ // Campaign Preview API Events
167
+ CAMPAIGN_PREVIEW_ACCEPT_SUCCESS: 'Campaign Preview Accept Success',
168
+ CAMPAIGN_PREVIEW_ACCEPT_FAILED: 'Campaign Preview Accept Failed',
169
+ CAMPAIGN_PREVIEW_SKIP_SUCCESS: 'Campaign Preview Skip Success',
170
+ CAMPAIGN_PREVIEW_SKIP_FAILED: 'Campaign Preview Skip Failed',
171
+ CAMPAIGN_PREVIEW_REMOVE_SUCCESS: 'Campaign Preview Remove Success',
172
+ CAMPAIGN_PREVIEW_REMOVE_FAILED: 'Campaign Preview Remove Failed',
173
+
174
+ // AI Assistant Transcript events
167
175
  AI_ASSISTANT_SEND_EVENT_SUCCESS: 'AI Assistant Send Event Success',
168
176
  AI_ASSISTANT_SEND_EVENT_FAILED: 'AI Assistant Send Event Failed',
169
177
  AI_ASSISTANT_GET_SUGGESTED_RESPONSE_SUCCESS: 'AI Assistant Get Suggested Response Success',
@@ -302,6 +302,9 @@ export class ApiAIAssistant {
302
302
  ['operational']
303
303
  );
304
304
 
305
+ if (error instanceof Error) {
306
+ throw error;
307
+ }
305
308
  const {error: detailedError} = getErrorDetails(
306
309
  error,
307
310
  METHODS.FETCH_HISTORIC_TRANSCRIPTS,
@@ -189,7 +189,7 @@ function parseAgentConfigs(profileData: {
189
189
 
190
190
  const finalData = {
191
191
  teams: teamData,
192
- defaultDn: userData.defaultDialledNumber,
192
+ defaultDn: userData.deafultDialledNumber,
193
193
  forceDefaultDn: tenantData.forceDefaultDn,
194
194
  forceDefaultDnForAgent: getDefaultAgentDN(agentProfileData.agentDNValidation),
195
195
  regexUS: tenantData.dnDefaultRegex,
@@ -119,6 +119,16 @@ export const CC_TASK_EVENTS = {
119
119
  AGENT_CONTACT_UNASSIGNED: 'AgentContactUnassigned',
120
120
  /** Event emitted when inviting agent fails */
121
121
  AGENT_INVITE_FAILED: 'AgentInviteFailed',
122
+ /** Event emitted when a campaign preview contact is offered to the agent */
123
+ AGENT_OFFER_CAMPAIGN_RESERVATION: 'AgentOfferCampaignReservation',
124
+ /** Event emitted when campaign contact is updated */
125
+ CAMPAIGN_CONTACT_UPDATED: 'CampaignContactUpdated',
126
+ /** Event emitted when accepting a campaign preview contact fails */
127
+ CAMPAIGN_PREVIEW_ACCEPT_FAILED: 'CampaignPreviewAcceptFailed',
128
+ /** Event emitted when skipping a campaign preview contact fails */
129
+ CAMPAIGN_PREVIEW_SKIP_FAILED: 'CampaignPreviewSkipFailed',
130
+ /** Event emitted when removing a campaign preview contact fails */
131
+ CAMPAIGN_PREVIEW_REMOVE_FAILED: 'CampaignPreviewRemoveFailed',
122
132
  /** Event emitted when a real-time transcript chunk is received */
123
133
  REAL_TIME_TRANSCRIPTION: 'REAL_TIME_TRANSCRIPTION',
124
134
  /** Event emitted when an AI assistant suggested response is available */
@@ -294,8 +304,9 @@ export type AgentResponse = {
294
304
 
295
305
  /**
296
306
  * The default dialed number of the agent.
307
+ * Note: The API returns this field as "deafultDialledNumber" (with typo).
297
308
  */
298
- defaultDialledNumber?: string;
309
+ deafultDialledNumber?: string;
299
310
  };
300
311
 
301
312
  /**
@@ -58,6 +58,7 @@ export const AGENT = 'agent';
58
58
  * @ignore
59
59
  */
60
60
  export const SUBSCRIBE_API = 'v1/notification/subscribe';
61
+
61
62
  /**
62
63
  * API path for realtime transcription subscription.
63
64
  * @type {string}
@@ -38,6 +38,9 @@ export type TaskErrorIds =
38
38
  | {'Service.aqm.task.pauseRecording': Failure}
39
39
  | {'Service.aqm.task.resumeRecording': Failure}
40
40
  | {'Service.aqm.dialer.startOutdial': Failure}
41
+ | {'Service.aqm.dialer.acceptPreviewContact': Failure}
42
+ | {'Service.aqm.dialer.skipPreviewContact': Failure}
43
+ | {'Service.aqm.dialer.removePreviewContact': Failure}
41
44
  | {'Service.reqs.generic.failure': {trackingId: string}};
42
45
 
43
46
  export type ReqError =
@@ -14,6 +14,7 @@ import {
14
14
  InteractionParticipant,
15
15
  } from '../task/types';
16
16
  import {PARTICIPANT_TYPES, STATE_CONSULT} from './constants';
17
+ import {DialPlan} from '../config/types';
17
18
 
18
19
  /**
19
20
  * Extracts common error details from a Webex request payload.
@@ -30,11 +31,72 @@ const getCommonErrorDetails = (errObj: WebexRequestPayload) => {
30
31
  };
31
32
  };
32
33
 
33
- export const isValidDialNumber = (input: string): boolean => {
34
- // This regex checks for a valid dial number format for only few countries such as US, Canada.
35
- const regexForDn = /1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}/;
34
+ /**
35
+ * Strips characters defined in the dial plan entry from the input string.
36
+ *
37
+ * @param input - The dial number to sanitize
38
+ * @param strippedChars - String of characters to remove from the input
39
+ * @returns The sanitized input with specified characters removed
40
+ */
41
+ export const stripDialPlanChars = (input: string, strippedChars: string): string => {
42
+ if (!strippedChars) {
43
+ return input;
44
+ }
45
+
46
+ const charsToStrip = new Set(strippedChars.split(''));
36
47
 
37
- return regexForDn.test(input);
48
+ return input
49
+ .split('')
50
+ .filter((c) => !charsToStrip.has(c))
51
+ .join('');
52
+ };
53
+
54
+ /**
55
+ * Validates a dial number against the provided dial plan regex patterns.
56
+ * A number is valid if it matches at least one regex pattern in the dial plans.
57
+ * Skips validation when no dial plan entries are configured, deferring to the server.
58
+ *
59
+ * @param input - The dial number to validate
60
+ * @param dialPlanEntries - Array of dial plan entries containing regex patterns
61
+ * @returns true if the input matches at least one dial plan regex pattern or no entries are configured, false otherwise
62
+ */
63
+ export const isValidDialNumber = (
64
+ input: string,
65
+ dialPlanEntries: DialPlan['dialPlanEntity']
66
+ ): boolean => {
67
+ if (!input) {
68
+ LoggerProxy.warn('Dial number is empty or undefined.', {
69
+ module: 'Utils',
70
+ method: 'isValidDialNumber',
71
+ });
72
+
73
+ return false;
74
+ }
75
+
76
+ if (!dialPlanEntries || dialPlanEntries.length === 0) {
77
+ LoggerProxy.log(
78
+ 'No dial plan entries found. Skipping client-side validation, deferring to server.',
79
+ {module: 'Utils', method: 'isValidDialNumber'}
80
+ );
81
+
82
+ return true;
83
+ }
84
+
85
+ return dialPlanEntries.some((entry) => {
86
+ try {
87
+ const sanitizedInput = stripDialPlanChars(input, entry.strippedChars);
88
+ const regex = new RegExp(entry.regex);
89
+
90
+ return regex.test(sanitizedInput);
91
+ } catch (e) {
92
+ LoggerProxy.warn(`Failed to validate dial number against entry "${entry.name}": ${e}`, {
93
+ module: 'Utils',
94
+ method: 'isValidDialNumber',
95
+ });
96
+
97
+ return false;
98
+ }
99
+ });
38
100
  };
39
101
 
40
102
  export const getStationLoginErrorData = (failure: Failure, loginOption: LoginOption) => {
@@ -56,7 +118,7 @@ export const getStationLoginErrorData = (failure: Failure, loginOption: LoginOpt
56
118
  },
57
119
  INVALID_DIAL_NUMBER: {
58
120
  message:
59
- 'Enter a valid US dial number. For help, reach out to your administrator or support team.',
121
+ 'Enter a valid dial number. For help, reach out to your administrator or support team.',
60
122
  fieldName: loginOption,
61
123
  },
62
124
  };
@@ -199,6 +261,7 @@ export const createErrDetailsObject = (errObj: WebexRequestPayload) => {
199
261
  };
200
262
 
201
263
  /*
264
+ /**
202
265
  * Gets the consulted agent ID from the media object by finding the agent
203
266
  * in the consult media participants (excluding the current agent).
204
267
  *
@@ -20,18 +20,40 @@ export default class AqmReqs {
20
20
  this.webSocketManager.on('message', this.onMessage.bind(this));
21
21
  }
22
22
 
23
+ /**
24
+ * Creates a request function for an API call with parameters
25
+ * @param c - The configuration for the request
26
+ * @returns A function that makes the API request
27
+ */
23
28
  req<TRes, TErr, TReq>(c: Conf<TRes, TErr, TReq>): Res<TRes, TReq> {
24
29
  return (p: TReq, cbRes?: CbRes<TRes>) => this.makeAPIRequest(c(p), cbRes);
25
30
  }
26
31
 
32
+ /**
33
+ * Creates a request function for an API call with no parameters
34
+ * @param c - The configuration for the request
35
+ * @returns A function that makes the API request
36
+ */
27
37
  reqEmpty<TRes, TErr>(c: ConfEmpty<TRes, TErr>): ResEmpty<TRes> {
28
38
  return (cbRes?: CbRes<TRes>) => this.makeAPIRequest(c(), cbRes);
29
39
  }
30
40
 
41
+ /**
42
+ * Makes an API request
43
+ * @param c - The request configuration
44
+ * @param cbRes - The callback for the response
45
+ * @returns A promise that resolves with the response or rejects with an error
46
+ */
31
47
  private async makeAPIRequest<TRes, TErr>(c: Req<TRes, TErr>, cbRes?: CbRes<TRes>): Promise<TRes> {
32
48
  return this.createPromise(c, cbRes);
33
49
  }
34
50
 
51
+ /**
52
+ * Creates a promise for an API request
53
+ * @param c - The request configuration
54
+ * @param cbRes - The callback for the response
55
+ * @returns A promise that resolves with the response or rejects with an error
56
+ */
35
57
  private createPromise<TRes, TErr>(c: Req<TRes, TErr>, cbRes?: CbRes<TRes>) {
36
58
  return new Promise<TRes>((resolve, reject) => {
37
59
  const keySuccess = this.bindPrint(c.notifSuccess.bind);
@@ -154,10 +176,13 @@ export default class AqmReqs {
154
176
  if (response?.headers) {
155
177
  response.headers.Authorization = '*';
156
178
  }
157
- LoggerProxy.error(`Routing request timeout${keySuccess}${response!}${c.url}`, {
158
- module: AQM_REQS_FILE,
159
- method: METHODS.CREATE_PROMISE,
160
- });
179
+ LoggerProxy.error(
180
+ `Routing request timeout${keySuccess}${JSON.stringify(response)}${c.url}`,
181
+ {
182
+ module: AQM_REQS_FILE,
183
+ method: METHODS.CREATE_PROMISE,
184
+ }
185
+ );
161
186
  reject(
162
187
  new Err.Details('Service.aqm.reqs.Timeout', {
163
188
  key: keySuccess,
@@ -171,39 +196,60 @@ export default class AqmReqs {
171
196
  });
172
197
  }
173
198
 
174
- private bindPrint(bind: any) {
199
+ /**
200
+ * Converts a bind object to a string representation
201
+ * @param bind - The bind object to convert
202
+ * @returns A string representation of the bind object
203
+ */
204
+ private bindPrint(bind: any): string {
175
205
  let result = '';
176
- // eslint-disable-next-line no-restricted-syntax
177
- for (const k in bind) {
178
- if (Array.isArray(bind[k])) {
179
- result += `${k}=[${bind[k].join(',')}],`;
180
- } else if (typeof bind[k] === 'object' && bind[k] !== null) {
181
- result += `${k}=(${this.bindPrint(bind[k])}),`;
206
+ for (const key of Object.keys(bind).filter((prop) => prop !== '__typeMap')) {
207
+ const value = bind[key];
208
+
209
+ if (Array.isArray(value)) {
210
+ result += `${key}=[${value.join(',')}],`;
211
+ } else if (typeof value === 'object' && value !== null) {
212
+ result += `${key}=(${this.bindPrint(value)}),`;
182
213
  } else {
183
- result += `${k}=${bind[k]},`;
214
+ result += `${key}=${value},`;
184
215
  }
185
216
  }
186
217
 
187
218
  return result ? result.slice(0, -1) : result;
188
219
  }
189
220
 
190
- private bindCheck(bind: any, msg: any) {
191
- // eslint-disable-next-line no-restricted-syntax
192
- for (const k in bind) {
193
- if (Array.isArray(bind[k])) {
221
+ /**
222
+ * Checks if a message matches a bind object
223
+ * @param bind - The bind object to check against
224
+ * @param msg - The message to check
225
+ * @returns True if the message matches the bind object, false otherwise
226
+ */
227
+ private bindCheck(bind: any, msg: any): boolean {
228
+ // Handle type-dependent field matching if __typeMap is present
229
+ if (bind.__typeMap && typeof bind.__typeMap === 'object') {
230
+ if (!AqmReqs.typeMapCheck(bind.__typeMap, msg)) {
231
+ return false;
232
+ }
233
+ }
234
+
235
+ for (const key of Object.keys(bind).filter((prop) => prop !== '__typeMap')) {
236
+ const bindValue = bind[key];
237
+ const msgValue = msg[key];
238
+
239
+ if (Array.isArray(bindValue)) {
194
240
  // Check if the message value matches any of the values in the array
195
- if (!bind[k].includes(msg[k])) {
241
+ if (!bindValue.includes(msgValue)) {
196
242
  return false;
197
243
  }
198
- } else if (typeof bind[k] === 'object' && bind[k] !== null) {
199
- if (typeof msg[k] === 'object' && msg[k] !== null) {
200
- if (!this.bindCheck(bind[k], msg[k])) {
244
+ } else if (typeof bindValue === 'object' && bindValue !== null) {
245
+ if (typeof msgValue === 'object' && msgValue !== null) {
246
+ if (!this.bindCheck(bindValue, msgValue)) {
201
247
  return false;
202
248
  }
203
249
  } else {
204
250
  return false;
205
251
  }
206
- } else if (!msg[k] || msg[k] !== bind[k]) {
252
+ } else if (!msgValue || msgValue !== bindValue) {
207
253
  return false;
208
254
  }
209
255
  }
@@ -211,7 +257,39 @@ export default class AqmReqs {
211
257
  return true;
212
258
  }
213
259
 
214
- // must be lambda
260
+ /**
261
+ * Checks type-dependent field conditions defined in __typeMap.
262
+ * @param typeMap - The type map to check against
263
+ * @param msg - The message to check
264
+ * @returns True if the message matches the type map, false otherwise
265
+ * The typeMap has the shape:
266
+ * { typeField: "type", conditions: { EventA: { field: value }, EventB: { field: value } } }
267
+ * It reads msg[typeField] to determine which condition set to apply,
268
+ * then verifies all fields in that condition match the message.
269
+ */
270
+ private static typeMapCheck(typeMap: any, msg: any): boolean {
271
+ const typeField = typeMap.typeField || 'type';
272
+ const msgType = msg[typeField];
273
+
274
+ if (typeMap.conditions && typeMap.conditions[msgType]) {
275
+ const condition = typeMap.conditions[msgType];
276
+ for (const field of Object.keys(condition)) {
277
+ if (!msg[field] || msg[field] !== condition[field]) {
278
+ return false;
279
+ }
280
+ }
281
+
282
+ return true;
283
+ }
284
+
285
+ return false;
286
+ }
287
+
288
+ /**
289
+ * Handles incoming messages from the WebSocket (must be a lambda fn)
290
+ * @param msg - The message to handle
291
+ * @returns
292
+ */
215
293
  private readonly onMessage = (msg: any) => {
216
294
  const event = JSON.parse(msg);
217
295
  if (event.type === 'Welcome') {
@@ -48,8 +48,16 @@ export class WebSocketManager extends EventEmitter {
48
48
  body: SubscribeRequest;
49
49
  resource: string;
50
50
  }): Promise<WelcomeResponse> {
51
- const {body, resource} = options;
52
- await this.register(body, resource);
51
+ const {body: connectionConfig, resource} = options;
52
+ try {
53
+ await this.register(connectionConfig, resource);
54
+ } catch (error) {
55
+ LoggerProxy.error(`[WebSocketStatus] | Error in registering Websocket ${error}`, {
56
+ module: WEB_SOCKET_MANAGER_FILE,
57
+ method: METHODS.INIT_WEB_SOCKET,
58
+ });
59
+ throw error;
60
+ }
53
61
 
54
62
  return new Promise((resolve, reject) => {
55
63
  this.welcomePromiseResolve = resolve;
@@ -81,11 +89,23 @@ export class WebSocketManager extends EventEmitter {
81
89
 
82
90
  private async register(connectionConfig: SubscribeRequest, resource: string) {
83
91
  try {
92
+ // X-ORGANIZATION-ID header is only required for INT environments
93
+ const isIntEnv = this.webex.internal?.services?.isIntegrationEnvironment() || false;
94
+ const orgId = this.webex.credentials.getOrgId();
95
+
96
+ if (isIntEnv && orgId) {
97
+ LoggerProxy.log(`[WebSocketManager] Adding X-ORGANIZATION-ID header for INT environment`, {
98
+ module: WEB_SOCKET_MANAGER_FILE,
99
+ method: METHODS.REGISTER,
100
+ });
101
+ }
102
+
84
103
  const subscribeResponse: SubscribeResponse = await this.webex.request({
85
104
  service: WCC_API_GATEWAY,
86
105
  resource,
87
106
  method: HTTP_METHODS.POST,
88
107
  body: connectionConfig,
108
+ headers: isIntEnv && orgId ? {'X-ORGANIZATION-ID': orgId} : undefined,
89
109
  });
90
110
  this.url = subscribeResponse.body.webSocketUrl;
91
111
  } catch (e) {
@@ -93,6 +113,7 @@ export class WebSocketManager extends EventEmitter {
93
113
  `Register API Failed, Request to RoutingNotifs websocket registration API failed ${e}`,
94
114
  {module: WEB_SOCKET_MANAGER_FILE, method: METHODS.REGISTER}
95
115
  );
116
+ throw e;
96
117
  }
97
118
  }
98
119
 
@@ -41,6 +41,7 @@ export default class Services {
41
41
  constructor(options: {webex: WebexSDK; connectionConfig: SubscribeRequest}) {
42
42
  const {webex, connectionConfig} = options;
43
43
  this.webSocketManager = new WebSocketManager({webex});
44
+ // TODO: Implement reconnection logic for this websocket in upcoming PR
44
45
  this.rtdWebSocketManager = new WebSocketManager({webex});
45
46
  const aqmReq = new AqmReqs(this.webSocketManager);
46
47
  this.config = new AgentConfigService();
@@ -414,6 +414,19 @@ export default abstract class Task extends EventEmitter implements ITask {
414
414
  emitTaskIncoming: this.createEmitSelfAction(TASK_EVENTS.TASK_INCOMING, {
415
415
  updateTaskData: true,
416
416
  }),
417
+ emitTaskCampaignPreviewReservation: this.createEmitSelfAction(
418
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
419
+ {updateTaskData: true}
420
+ ),
421
+ emitTaskCampaignPreviewAcceptFailed: this.createEmitSelfAction(
422
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED
423
+ ),
424
+ emitTaskCampaignPreviewSkipFailed: this.createEmitSelfAction(
425
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_SKIP_FAILED
426
+ ),
427
+ emitTaskCampaignPreviewRemoveFailed: this.createEmitSelfAction(
428
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED
429
+ ),
417
430
  emitTaskHydrate: this.createEmitSelfAction(TASK_EVENTS.TASK_HYDRATE, {
418
431
  updateTaskData: true,
419
432
  }),