@webex/calling 3.12.0-mobius-socket.11 → 3.12.0-mobius-socket.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 (41) hide show
  1. package/dist/CallingClient/CallingClient.js +21 -30
  2. package/dist/CallingClient/CallingClient.js.map +1 -1
  3. package/dist/CallingClient/calling/call.test.js +1 -1
  4. package/dist/CallingClient/calling/call.test.js.map +1 -1
  5. package/dist/CallingClient/constants.js +15 -3
  6. package/dist/CallingClient/constants.js.map +1 -1
  7. package/dist/CallingClient/line/line.test.js +1 -1
  8. package/dist/CallingClient/line/line.test.js.map +1 -1
  9. package/dist/CallingClient/registration/register.js +200 -116
  10. package/dist/CallingClient/registration/register.js.map +1 -1
  11. package/dist/CallingClient/registration/register.test.js +1 -1
  12. package/dist/CallingClient/registration/register.test.js.map +1 -1
  13. package/dist/CallingClient/registration/types.js.map +1 -1
  14. package/dist/CallingClient/utils/mobiusSocketMapper.js +32 -20
  15. package/dist/CallingClient/utils/mobiusSocketMapper.js.map +1 -1
  16. package/dist/CallingClient/utils/request.js +32 -3
  17. package/dist/CallingClient/utils/request.js.map +1 -1
  18. package/dist/CallingClient/utils/wsFeatureFlag.js +9 -1
  19. package/dist/CallingClient/utils/wsFeatureFlag.js.map +1 -1
  20. package/dist/common/Utils.js +70 -30
  21. package/dist/common/Utils.js.map +1 -1
  22. package/dist/module/CallingClient/CallingClient.js +13 -18
  23. package/dist/module/CallingClient/constants.js +12 -0
  24. package/dist/module/CallingClient/registration/register.js +101 -40
  25. package/dist/module/CallingClient/utils/mobiusSocketMapper.js +11 -0
  26. package/dist/module/CallingClient/utils/request.js +29 -1
  27. package/dist/module/CallingClient/utils/wsFeatureFlag.js +9 -2
  28. package/dist/module/common/Utils.js +17 -4
  29. package/dist/types/CallingClient/CallingClient.d.ts +0 -1
  30. package/dist/types/CallingClient/CallingClient.d.ts.map +1 -1
  31. package/dist/types/CallingClient/constants.d.ts +12 -0
  32. package/dist/types/CallingClient/constants.d.ts.map +1 -1
  33. package/dist/types/CallingClient/registration/register.d.ts.map +1 -1
  34. package/dist/types/CallingClient/registration/types.d.ts +1 -1
  35. package/dist/types/CallingClient/registration/types.d.ts.map +1 -1
  36. package/dist/types/CallingClient/utils/mobiusSocketMapper.d.ts.map +1 -1
  37. package/dist/types/CallingClient/utils/request.d.ts.map +1 -1
  38. package/dist/types/CallingClient/utils/wsFeatureFlag.d.ts.map +1 -1
  39. package/dist/types/common/Utils.d.ts +4 -1
  40. package/dist/types/common/Utils.d.ts.map +1 -1
  41. package/package.json +2 -1
@@ -79,6 +79,11 @@ export class CallingClient extends Eventing {
79
79
  this.registerCallsClearedListener();
80
80
  }
81
81
  async init() {
82
+ const loggerContext = {
83
+ file: CALLING_CLIENT_FILE,
84
+ method: METHODS.INIT,
85
+ };
86
+ log.info(METHOD_START_MESSAGE, loggerContext);
82
87
  if (typeof window !== 'undefined' && window?.navigator?.userAgent) {
83
88
  const ua = window.navigator.userAgent;
84
89
  if (ua.toLowerCase().includes('windows')) {
@@ -107,10 +112,11 @@ export class CallingClient extends Eventing {
107
112
  await this.getMobiusServers();
108
113
  if (this.apiRequest.isSocketEnabled()) {
109
114
  await this.connectToMobiusSocket();
110
- this.registerMobiusSocketListener();
115
+ this.apiRequest.registerMobiusSocketListener(this.handleMobiusAsyncEvent);
111
116
  }
112
117
  await this.createLine();
113
118
  this.setupNetworkEventListeners();
119
+ log.log('CallingClient initialization complete', loggerContext);
114
120
  }
115
121
  async checkNetworkReachability() {
116
122
  try {
@@ -411,34 +417,23 @@ export class CallingClient extends Eventing {
411
417
  }
412
418
  log.warn('All Mobius socket connection attempts exhausted for both primary and backup, continuing without socket', loggerContext);
413
419
  }
414
- registerMobiusSocketListener() {
415
- log.info(METHOD_START_MESSAGE, {
416
- file: CALLING_CLIENT_FILE,
417
- method: METHODS.REGISTER_MOBIUS_SOCKET_LISTENER,
418
- });
419
- this.apiRequest.registerMobiusSocketListener(this.handleMobiusAsyncEvent);
420
- log.info('Successfully registered listener for Mobius events', {
421
- file: CALLING_CLIENT_FILE,
422
- method: METHODS.REGISTER_MOBIUS_SOCKET_LISTENER,
423
- });
424
- }
425
420
  handleMobiusAsyncEvent = async (event) => {
426
- log.info(METHOD_START_MESSAGE, {
421
+ const loggerContext = {
427
422
  file: CALLING_CLIENT_FILE,
428
423
  method: METHODS.HANDLE_MOBIUS_ASYNC_EVENT,
429
- });
424
+ };
430
425
  const eventType = event?.data.eventType;
426
+ log.trace(`Mobius async event received - eventType: ${eventType ?? 'undefined'}`, loggerContext);
431
427
  if (!eventType) {
432
- log.warn('Dropping unsupported mobius socket payload', {
433
- file: CALLING_CLIENT_FILE,
434
- method: METHODS.HANDLE_MOBIUS_ASYNC_EVENT,
435
- });
428
+ log.warn('Dropping unsupported mobius socket payload', loggerContext);
436
429
  return;
437
430
  }
438
431
  if (eventType === MobiusEventType.REGISTRATION_DOWN) {
432
+ log.warn('Received REGISTRATION_DOWN event from Mobius; teardown handling pending (TODO)', loggerContext);
439
433
  return;
440
434
  }
441
435
  this.callManager.dequeueWsEvents(event);
436
+ log.trace(`Mobius async event dispatched to CallManager - eventType: ${eventType}`, loggerContext);
442
437
  };
443
438
  registerCallsClearedListener() {
444
439
  log.info(METHOD_START_MESSAGE, {
@@ -70,6 +70,8 @@ export const CALL_MANAGER_FILE = 'callManager';
70
70
  export const METRIC_FILE = 'metric';
71
71
  export const REGISTRATION_FILE = 'register';
72
72
  export const REQUEST_FILE = 'REQUEST';
73
+ export const MOBIUS_SOCKET_MAPPER_FILE = 'mobiusSocketMapper';
74
+ export const WS_FEATURE_FLAG_FILE = 'wsFeatureFlag';
73
75
  export const CODEC_ID = 'codecId';
74
76
  export const MEDIA_ID = 'id';
75
77
  export const RTC_ICE_CANDIDATE_PAIR = 'RTCIceCandidatePair_';
@@ -236,4 +238,14 @@ export const METHODS = {
236
238
  UNREGISTER_MOBIUS_SOCKET_LISTENER: 'unregisterMobiusSocketListener',
237
239
  HANDLE_MOBIUS_ASYNC_EVENT: 'handleMobiusAsyncEvent',
238
240
  HANDLE_REGISTRATION_DOWN_EVENT: 'handleRegistrationDownEvent',
241
+ DISCONNECT_FROM_MOBIUS_SOCKET: 'disconnectFromMobiusSocket',
242
+ MAKE_REQUEST: 'makeRequest',
243
+ DERIVE_MOBIUS_SOCKET_MESSAGE_TYPE: 'deriveMobiusSocketMessageType',
244
+ IS_MOBIUS_WSS_ENABLED: 'isMobiusWssEnabled',
245
+ RESTORE_PREVIOUS_REGISTRATION: 'restorePreviousRegistration',
246
+ RESTART_REGISTRATION: 'restartRegistration',
247
+ TRIGGER_REGISTRATION: 'triggerRegistration',
248
+ HANDLE_404_KEEPALIVE_FAILURE: 'handle404KeepaliveFailure',
249
+ INITIATE_FAILBACK: 'initiateFailback',
250
+ EXECUTE_FAILBACK: 'executeFailback',
239
251
  };
@@ -179,7 +179,22 @@ export class Registration {
179
179
  });
180
180
  }
181
181
  async restorePreviousRegistration(caller) {
182
+ const loggerContext = {
183
+ file: REGISTRATION_FILE,
184
+ method: METHODS.RESTORE_PREVIOUS_REGISTRATION,
185
+ };
186
+ log.info(`${METHOD_START_MESSAGE} - caller: ${caller}, activeMobiusUrl: ${this.activeMobiusUrl}`, loggerContext);
182
187
  let abort = false;
188
+ if (this.apiRequest.isSocketEnabled()) {
189
+ log.info(`Disconnecting from Mobius socket to restore previous registration.`, {
190
+ file: REGISTRATION_FILE,
191
+ method: 'restorePreviousRegistration',
192
+ });
193
+ await this.apiRequest.disconnectFromMobiusSocket({
194
+ code: 3050,
195
+ reason: 'done (permanent)',
196
+ });
197
+ }
183
198
  if (this.activeMobiusUrl) {
184
199
  abort = await this.attemptRegistrationWithServers(caller, [this.activeMobiusUrl]);
185
200
  if (this.retryAfter) {
@@ -193,20 +208,31 @@ export class Registration {
193
208
  abort = await this.attemptRegistrationWithServers(caller, this.backupMobiusUris);
194
209
  }
195
210
  else {
196
- this.restartRegistration(caller);
211
+ await this.restartRegistration(caller);
197
212
  }
198
213
  this.retryAfter = undefined;
214
+ log.log(`restorePreviousRegistration completed via 429 retry-after path - caller: ${caller}`, loggerContext);
199
215
  return true;
200
216
  }
201
217
  }
218
+ log.log(`restorePreviousRegistration completed - caller: ${caller}, deviceRegistered: ${this.isDeviceRegistered()}, abort: ${abort}`, loggerContext);
202
219
  return abort;
203
220
  }
204
221
  async handle404KeepaliveFailure(caller) {
222
+ const loggerContext = {
223
+ file: REGISTRATION_FILE,
224
+ method: METHODS.HANDLE_404_KEEPALIVE_FAILURE,
225
+ };
226
+ log.info(`${METHOD_START_MESSAGE} - caller: ${caller}`, loggerContext);
205
227
  if (caller === KEEPALIVE_UTIL) {
206
228
  const abort = await this.attemptRegistrationWithServers(caller);
207
229
  if (!abort && !this.isDeviceRegistered()) {
230
+ log.warn('Keepalive 404 recovery: re-registration did not complete, starting failover timer', loggerContext);
208
231
  await this.startFailoverTimer();
209
232
  }
233
+ else {
234
+ log.log(`Keepalive 404 recovery handled - deviceRegistered: ${this.isDeviceRegistered()}, abort: ${abort}`, loggerContext);
235
+ }
210
236
  }
211
237
  }
212
238
  async handle429Retry(retryAfter, caller) {
@@ -287,6 +313,14 @@ export class Registration {
287
313
  log.log(`Scheduled retry with primary in ${interval} seconds, number of attempts : ${attempt}`, loggerContext);
288
314
  }
289
315
  else if (this.backupMobiusUris.length) {
316
+ if (this.apiRequest.isSocketEnabled()) {
317
+ log.info('Disconnecting from primary Mobius socket for failover to backup servers', loggerContext);
318
+ await this.apiRequest.disconnectFromMobiusSocket({
319
+ code: 3050,
320
+ reason: 'done (permanent)',
321
+ });
322
+ log.log('Mobius socket disconnect complete prior to backup failover', loggerContext);
323
+ }
290
324
  this.saveFailoverState({
291
325
  attempt,
292
326
  timeElapsed,
@@ -384,14 +418,23 @@ export class Registration {
384
418
  this.rehomingIntervalMin);
385
419
  }
386
420
  initiateFailback() {
421
+ const loggerContext = {
422
+ file: REGISTRATION_FILE,
423
+ method: METHODS.INITIATE_FAILBACK,
424
+ };
387
425
  if (this.isFailbackRequired()) {
388
426
  if (!this.failbackTimer) {
389
427
  this.failback429RetryAttempts = 0;
390
428
  const intervalInMinutes = this.getFailbackInterval();
429
+ log.info(`Scheduling failback to primary - intervalMinutes: ${intervalInMinutes}`, loggerContext);
391
430
  this.startFailbackTimer(intervalInMinutes * MINUTES_TO_SEC_MFACTOR);
392
431
  }
432
+ else {
433
+ log.info('Failback timer already scheduled, skipping', loggerContext);
434
+ }
393
435
  }
394
436
  else {
437
+ log.info('Failback not required, clearing any pending failback timer', loggerContext);
395
438
  this.failback429RetryAttempts = 0;
396
439
  this.clearFailbackTimer();
397
440
  }
@@ -404,20 +447,24 @@ export class Registration {
404
447
  });
405
448
  }
406
449
  async executeFailback() {
450
+ const loggerContext = {
451
+ file: REGISTRATION_FILE,
452
+ method: METHODS.EXECUTE_FAILBACK,
453
+ };
454
+ log.info(METHOD_START_MESSAGE, loggerContext);
407
455
  await this.mutex.runExclusive(async () => {
408
456
  if (this.isFailbackRequired()) {
409
457
  const primaryServerStatus = await this.isPrimaryActive();
410
458
  if (Object.keys(this.callManager.getActiveCalls()).length === 0 && primaryServerStatus) {
411
- log.info(`Attempting failback to primary.`, {
412
- file: REGISTRATION_FILE,
413
- method: this.executeFailback.name,
414
- });
459
+ log.info(`Attempting failback to primary.`, loggerContext);
415
460
  await this.deregister();
416
461
  if (this.apiRequest.isSocketEnabled()) {
462
+ log.info('Disconnecting from backup Mobius socket for failback to primary', loggerContext);
417
463
  await this.apiRequest.disconnectFromMobiusSocket({
418
464
  code: 3050,
419
465
  reason: 'done (permanent)',
420
466
  });
467
+ log.log('Mobius socket disconnect complete prior to primary failback', loggerContext);
421
468
  }
422
469
  const abort = await this.attemptRegistrationWithServers(FAILBACK_UTIL);
423
470
  if (this.scheduled429Retry || abort || this.isDeviceRegistered()) {
@@ -429,7 +476,7 @@ export class Registration {
429
476
  return;
430
477
  }
431
478
  if (!this.isDeviceRegistered()) {
432
- await this.restartRegistration(this.executeFailback.name);
479
+ await this.restartRegistration(loggerContext.method);
433
480
  }
434
481
  else {
435
482
  this.failbackTimer = undefined;
@@ -437,10 +484,7 @@ export class Registration {
437
484
  }
438
485
  }
439
486
  else {
440
- log.info('Active calls present or primary Mobius is down, deferring failback to next cycle.', {
441
- file: REGISTRATION_FILE,
442
- method: this.executeFailback.name,
443
- });
487
+ log.info('Active calls present or primary Mobius is down, deferring failback to next cycle.', loggerContext);
444
488
  this.failbackTimer = undefined;
445
489
  this.initiateFailback();
446
490
  }
@@ -470,12 +514,18 @@ export class Registration {
470
514
  this.registrationStatus = value;
471
515
  }
472
516
  async restartRegistration(caller) {
517
+ const loggerContext = {
518
+ file: REGISTRATION_FILE,
519
+ method: METHODS.RESTART_REGISTRATION,
520
+ };
521
+ log.info(`${METHOD_START_MESSAGE} - caller: ${caller}`, loggerContext);
473
522
  this.clearFailbackTimer();
474
523
  this.failback429RetryAttempts = 0;
475
524
  const abort = await this.attemptRegistrationWithServers(caller, this.primaryMobiusUris);
476
525
  if (!abort && !this.isDeviceRegistered()) {
477
526
  await this.startFailoverTimer();
478
527
  }
528
+ log.log(`restartRegistration completed - caller: ${caller}, deviceRegistered: ${this.isDeviceRegistered()}, abort: ${abort}`, loggerContext);
479
529
  }
480
530
  async handleConnectionRestoration(retry) {
481
531
  log.info(METHOD_START_MESSAGE, {
@@ -528,7 +578,13 @@ export class Registration {
528
578
  };
529
579
  }
530
580
  async triggerRegistration() {
581
+ const loggerContext = {
582
+ file: REGISTRATION_FILE,
583
+ method: METHODS.TRIGGER_REGISTRATION,
584
+ };
585
+ log.info(METHOD_START_MESSAGE, loggerContext);
531
586
  if (await this.resumeFailover()) {
587
+ log.info('Registration trigger handled by resumeFailover path', loggerContext);
532
588
  return;
533
589
  }
534
590
  if (this.primaryMobiusUris.length > 0) {
@@ -536,9 +592,17 @@ export class Registration {
536
592
  if (!this.isDeviceRegistered() && !abort) {
537
593
  await this.startFailoverTimer();
538
594
  }
595
+ log.log(`triggerRegistration completed - deviceRegistered: ${this.isDeviceRegistered()}, abort: ${abort}`, loggerContext);
596
+ }
597
+ else {
598
+ log.warn('triggerRegistration skipped: no primary Mobius URIs available', loggerContext);
539
599
  }
540
600
  }
541
601
  async attemptRegistrationWithServers(caller, servers = this.primaryMobiusUris) {
602
+ const loggerContext = {
603
+ file: REGISTRATION_FILE,
604
+ method: REGISTER_UTIL,
605
+ };
542
606
  let abort = false;
543
607
  this.retryAfter = undefined;
544
608
  let connectedWebSocketUrl;
@@ -546,10 +610,7 @@ export class Registration {
546
610
  return abort;
547
611
  }
548
612
  if (this.isDeviceRegistered()) {
549
- log.info(`[${caller}] : Device already registered with : ${this.activeMobiusUrl}`, {
550
- file: REGISTRATION_FILE,
551
- method: REGISTER_UTIL,
552
- });
613
+ log.info(`[${caller}] : Device already registered with : ${this.activeMobiusUrl}`, loggerContext);
553
614
  return abort;
554
615
  }
555
616
  for (const url of servers) {
@@ -560,14 +621,13 @@ export class Registration {
560
621
  abort = false;
561
622
  this.registrationStatus = RegistrationStatus.INACTIVE;
562
623
  this.lineEmitter(LINE_EVENTS.CONNECTING);
563
- log.info(`[${caller}] : Mobius url to contact: ${url}`, {
564
- file: REGISTRATION_FILE,
565
- method: REGISTER_UTIL,
566
- });
624
+ log.info(`[${caller}] : Mobius url to contact: ${url}`, loggerContext);
567
625
  if (this.apiRequest.isSocketEnabled()) {
568
626
  const wssNormalizedUrl = url.endsWith('/') ? url.slice(0, -1) : url;
627
+ log.info(`[${caller}] : Connecting to Mobius WebSocket - serverType: ${serverType}, wssUrl: ${wssNormalizedUrl}`, loggerContext);
569
628
  connectedWebSocketUrl = await this.apiRequest.connectToMobiusSocket(wssNormalizedUrl);
570
629
  connectedWebSocketUrl = connectedWebSocketUrl ? `${connectedWebSocketUrl}/` : undefined;
630
+ log.log(`[${caller}] : Mobius WebSocket connected - serverType: ${serverType}, connectedWebSocketUrl: ${connectedWebSocketUrl}`, loggerContext);
571
631
  }
572
632
  const resp = await this.postRegistration(url);
573
633
  this.clearFailoverState();
@@ -575,10 +635,7 @@ export class Registration {
575
635
  this.registrationStatus = RegistrationStatus.ACTIVE;
576
636
  this.setActiveMobiusUrl(connectedWebSocketUrl || url);
577
637
  this.lineEmitter(LINE_EVENTS.REGISTERED, resp.body);
578
- log.log(`Registration successful for deviceId: ${this.deviceInfo.device?.deviceId} userId: ${this.userId} responseTrackingId: ${resp.headers?.trackingid}`, {
579
- file: REGISTRATION_FILE,
580
- method: METHODS.REGISTER,
581
- });
638
+ log.log(`Registration successful for deviceId: ${this.deviceInfo.device?.deviceId} userId: ${this.userId} responseTrackingId: ${resp.headers?.trackingid}`, loggerContext);
582
639
  this.setIntervalValues(this.deviceInfo);
583
640
  this.metricManager.setDeviceInfo(this.deviceInfo);
584
641
  this.metricManager.submitRegistrationMetric(METRIC_EVENT.REGISTRATION, REG_ACTION.REGISTER, METRIC_TYPE.BEHAVIORAL, caller, serverType, resp.headers?.trackingid ?? '', undefined, undefined);
@@ -587,25 +644,30 @@ export class Registration {
587
644
  break;
588
645
  }
589
646
  catch (err) {
590
- connectedWebSocketUrl = undefined;
591
- await this.apiRequest.disconnectFromMobiusSocket({ code: 3050, reason: 'done (permanent)' });
592
647
  const body = err;
593
- abort = await handleRegistrationErrors(body, (clientError, finalError) => {
594
- if (finalError) {
648
+ const { finalError, shouldDisconnect } = await handleRegistrationErrors(body, (clientError, isFinalError) => {
649
+ if (isFinalError) {
595
650
  this.lineEmitter(LINE_EVENTS.ERROR, undefined, clientError);
596
651
  }
597
652
  else {
598
653
  this.lineEmitter(LINE_EVENTS.UNREGISTERED);
599
654
  }
600
655
  this.metricManager.submitRegistrationMetric(METRIC_EVENT.REGISTRATION_ERROR, REG_ACTION.REGISTER, METRIC_TYPE.BEHAVIORAL, caller, serverType, body.headers?.trackingid ?? '', undefined, clientError);
601
- }, { method: caller, file: REGISTRATION_FILE }, (retryAfter, retryCaller) => this.handle429Retry(retryAfter, retryCaller), this.restoreRegistrationCallBack());
656
+ }, { method: caller, file: REGISTRATION_FILE }, (retryAfter, retryCaller) => this.handle429Retry(retryAfter, retryCaller), this.restoreRegistrationCallBack(), servers.length);
657
+ abort = finalError;
602
658
  if (this.registrationStatus === RegistrationStatus.ACTIVE) {
603
- log.info(`[${caller}] : Device is already restored, active mobius url: ${this.activeMobiusUrl}`, {
604
- file: REGISTRATION_FILE,
605
- method: this.attemptRegistrationWithServers.name,
606
- });
659
+ log.info(`[${caller}] : Device is already restored, active mobius url: ${this.activeMobiusUrl}`, loggerContext);
607
660
  break;
608
661
  }
662
+ if (shouldDisconnect && this.apiRequest.isSocketEnabled()) {
663
+ connectedWebSocketUrl = undefined;
664
+ log.info(`[${caller}] : Tearing down Mobius WebSocket after registration error - serverType: ${serverType}`, loggerContext);
665
+ await this.apiRequest.disconnectFromMobiusSocket({
666
+ code: 3050,
667
+ reason: 'done (permanent)',
668
+ });
669
+ log.log(`[${caller}] : Mobius WebSocket disconnect complete after registration error - serverType: ${serverType}`, loggerContext);
670
+ }
609
671
  if (abort) {
610
672
  this.setStatus(RegistrationStatus.INACTIVE);
611
673
  await uploadLogs();
@@ -666,7 +728,7 @@ export class Registration {
666
728
  if (event.data.type === WorkerMessageType.KEEPALIVE_FAILURE) {
667
729
  const error = event.data.err;
668
730
  log.warn(`Keep-alive missed ${event.data.keepAliveRetryCount} times. Status -> ${error.statusCode} `, logContext);
669
- const abort = await handleRegistrationErrors(error, (clientError, finalError) => {
731
+ const { finalError: abort } = await handleRegistrationErrors(error, (clientError, finalError) => {
670
732
  if (finalError) {
671
733
  this.lineEmitter(LINE_EVENTS.ERROR, undefined, clientError);
672
734
  }
@@ -706,18 +768,17 @@ export class Registration {
706
768
  return this.reconnectPending;
707
769
  }
708
770
  async deregister() {
771
+ const loggerContext = {
772
+ file: REGISTRATION_FILE,
773
+ method: METHODS.DEREGISTER,
774
+ };
775
+ log.info(METHOD_START_MESSAGE, loggerContext);
709
776
  try {
710
777
  await this.deleteRegistration(this.activeMobiusUrl, this.deviceInfo.device?.deviceId, this.deviceInfo.device?.clientDeviceUri);
711
- log.log('Registration successfully deregistered', {
712
- file: REGISTRATION_FILE,
713
- method: METHODS.DEREGISTER,
714
- });
778
+ log.log('Registration successfully deregistered', loggerContext);
715
779
  }
716
780
  catch (err) {
717
- log.warn(`Delete failed with Mobius: ${JSON.stringify(err)}`, {
718
- file: REGISTRATION_FILE,
719
- method: METHODS.DEREGISTER,
720
- });
781
+ log.warn(`Delete failed with Mobius: ${JSON.stringify(err)}`, loggerContext);
721
782
  }
722
783
  this.clearKeepaliveTimer();
723
784
  this.setStatus(RegistrationStatus.INACTIVE);
@@ -1,7 +1,14 @@
1
1
  import { HTTP_METHODS } from '../../common/types';
2
+ import log from '../../Logger';
3
+ import { METHODS, MOBIUS_SOCKET_MAPPER_FILE } from '../constants';
2
4
  import { MOBIUS_SOCKET_MESSAGE_TYPE } from './constants';
3
5
  export function deriveMobiusSocketMessageType(uri, httpMethodType) {
6
+ const logContext = {
7
+ file: MOBIUS_SOCKET_MAPPER_FILE,
8
+ method: METHODS.DERIVE_MOBIUS_SOCKET_MESSAGE_TYPE,
9
+ };
4
10
  if (!uri) {
11
+ log.warn('Cannot derive Mobius socket message type: uri is empty', logContext);
5
12
  return MOBIUS_SOCKET_MESSAGE_TYPE.UNKNOWN;
6
13
  }
7
14
  if (uri.includes('/services')) {
@@ -14,6 +21,7 @@ export function deriveMobiusSocketMessageType(uri, httpMethodType) {
14
21
  if (uri.includes('/calltransfer/commit')) {
15
22
  return MOBIUS_SOCKET_MESSAGE_TYPE.CALL_TRANSFER;
16
23
  }
24
+ log.warn(`Unrecognized supplementary service uri - uri: ${uri}, httpMethod: ${httpMethodType}`, logContext);
17
25
  return MOBIUS_SOCKET_MESSAGE_TYPE.UNKNOWN;
18
26
  }
19
27
  if (uri.includes('/calls/') && uri.includes('/media')) {
@@ -29,6 +37,7 @@ export function deriveMobiusSocketMessageType(uri, httpMethodType) {
29
37
  if (httpMethodType === HTTP_METHODS.DELETE) {
30
38
  return MOBIUS_SOCKET_MESSAGE_TYPE.CALL_DELETE;
31
39
  }
40
+ log.warn(`Unrecognized httpMethod for calls/{callId} - uri: ${uri}, httpMethod: ${httpMethodType}`, logContext);
32
41
  return MOBIUS_SOCKET_MESSAGE_TYPE.UNKNOWN;
33
42
  }
34
43
  if (uri.endsWith('/call')) {
@@ -47,11 +56,13 @@ export function deriveMobiusSocketMessageType(uri, httpMethodType) {
47
56
  if (httpMethodType === HTTP_METHODS.GET) {
48
57
  return MOBIUS_SOCKET_MESSAGE_TYPE.DEVICE_GET;
49
58
  }
59
+ log.warn(`Unrecognized httpMethod for devices/{deviceId} - uri: ${uri}, httpMethod: ${httpMethodType}`, logContext);
50
60
  return MOBIUS_SOCKET_MESSAGE_TYPE.UNKNOWN;
51
61
  }
52
62
  if (uri.includes('/devices') && !uri.match(/\/devices\/[^/?]+/)) {
53
63
  return MOBIUS_SOCKET_MESSAGE_TYPE.DEVICE_LIST;
54
64
  }
65
+ log.warn(`Unrecognized uri pattern for Mobius socket - uri: ${uri}, httpMethod: ${httpMethodType}`, logContext);
55
66
  return MOBIUS_SOCKET_MESSAGE_TYPE.UNKNOWN;
56
67
  }
57
68
  export const isSupplementaryServiceMessageType = (type) => [
@@ -41,12 +41,18 @@ export class APIRequest {
41
41
  APIRequest.instance = undefined;
42
42
  }
43
43
  constructor(config) {
44
+ const logContext = {
45
+ file: REQUEST_FILE,
46
+ method: METHODS.CONSTRUCTOR,
47
+ };
44
48
  if (!config.webex) {
49
+ log.error('APIRequest instantiation failed: WebexSDK instance is required', logContext);
45
50
  throw new Error('WebexSDK instance is required');
46
51
  }
47
52
  this.webex = config.webex;
48
53
  this.isMobiusSocketEnabled = isMobiusWssEnabled(config.webex) || false;
49
54
  this.mobiusSocket = getMobiusSocketInstance(this.webex);
55
+ log.info(`APIRequest initialized with transport: ${this.isMobiusSocketEnabled ? 'WSS' : 'HTTP'}`, logContext);
50
56
  }
51
57
  isSocketEnabled() {
52
58
  return this.isMobiusSocketEnabled;
@@ -74,8 +80,9 @@ export class APIRequest {
74
80
  async disconnectFromMobiusSocket(options) {
75
81
  const logContext = {
76
82
  file: REQUEST_FILE,
77
- method: 'disconnectFromMobiusSocket',
83
+ method: METHODS.DISCONNECT_FROM_MOBIUS_SOCKET,
78
84
  };
85
+ log.info('Disconnecting from Mobius WebSocket', logContext);
79
86
  try {
80
87
  await this.mobiusSocket.disconnect(options);
81
88
  log.log('Mobius WebSocket disconnected successfully', logContext);
@@ -85,10 +92,16 @@ export class APIRequest {
85
92
  }
86
93
  }
87
94
  async makeRequest(request) {
95
+ const logContext = {
96
+ file: REQUEST_FILE,
97
+ method: METHODS.MAKE_REQUEST,
98
+ };
99
+ log.info(`Dispatching request via ${this.isMobiusSocketEnabled ? 'WSS' : 'HTTP'} `, logContext);
88
100
  if (this.isMobiusSocketEnabled) {
89
101
  const trackingId = `webex-js-sdk_${uuid()}`;
90
102
  const socketType = deriveMobiusSocketMessageType(request.uri, request.method);
91
103
  if (socketType === MOBIUS_SOCKET_MESSAGE_TYPE.UNKNOWN) {
104
+ log.error(`Unknown Mobius Socket message type for uri: ${request.uri}, httpMethod: ${request.method}`, logContext);
92
105
  throw new Error(`Unknown Mobius Socket message type: ${socketType}`);
93
106
  }
94
107
  const isSupplementaryService = isSupplementaryServiceMessageType(socketType);
@@ -103,21 +116,36 @@ export class APIRequest {
103
116
  },
104
117
  data: request.body,
105
118
  });
119
+ log.log(`WSS request succeeded - socketType: ${socketType}, trackingId: ${trackingId}, statusCode: ${wsResponse.statusCode}`, logContext);
106
120
  return normalizeWsResponse(wsResponse);
107
121
  }
108
122
  catch (err) {
123
+ log.error(`WSS request failed - socketType: ${socketType}, trackingId: ${trackingId}, error: ${String(err)}`, logContext);
109
124
  throw normalizeWsError(err);
110
125
  }
111
126
  }
112
127
  return this.webex.request(request);
113
128
  }
114
129
  registerMobiusSocketListener(cb) {
130
+ const logContext = {
131
+ file: REQUEST_FILE,
132
+ method: METHODS.REGISTER_MOBIUS_SOCKET_LISTENER,
133
+ };
134
+ log.info('Attaching Mobius async event listener', logContext);
115
135
  this.mobiusSocket.on('event:async_event', (data) => {
136
+ log.trace(`Mobius async event received - eventType: ${data?.data?.eventType ?? 'unknown'}`, logContext);
116
137
  cb(data);
117
138
  });
139
+ log.log('Mobius async event listener attached', logContext);
118
140
  }
119
141
  unregisterMobiusSocketListener() {
142
+ const logContext = {
143
+ file: REQUEST_FILE,
144
+ method: METHODS.UNREGISTER_MOBIUS_SOCKET_LISTENER,
145
+ };
146
+ log.info('Detaching Mobius async event listener', logContext);
120
147
  this.mobiusSocket.off('event:async_event');
148
+ log.log('Mobius async event listener detached', logContext);
121
149
  }
122
150
  }
123
151
  export const createAPIRequest = (config) => APIRequest.getInstance(config);
@@ -1,5 +1,12 @@
1
+ import log from '../../Logger';
2
+ import { METHODS, WS_FEATURE_FLAG_FILE } from '../constants';
1
3
  export const WEBRTC_CALLING_OVER_WS_FEATURE_KEY = 'webrtc-calling-over-ws-CALL-219562';
2
4
  export function isMobiusWssEnabled(webex) {
3
- return (webex.internal?.device?.features?.developer?.get(WEBRTC_CALLING_OVER_WS_FEATURE_KEY)?.value ===
4
- true);
5
+ const enabled = webex.internal?.device?.features?.developer?.get(WEBRTC_CALLING_OVER_WS_FEATURE_KEY)?.value ===
6
+ true;
7
+ log.trace(`Mobius WSS feature flag '${WEBRTC_CALLING_OVER_WS_FEATURE_KEY}' resolved to: ${enabled}`, {
8
+ file: WS_FEATURE_FLAG_FILE,
9
+ method: METHODS.IS_MOBIUS_WSS_ENABLED,
10
+ });
11
+ return enabled;
5
12
  }
@@ -106,7 +106,8 @@ export function emitFinalFailure(emitterCb, loggerContext, message) {
106
106
  'An unknown error occurred. Wait a moment and try again. Please contact the administrator if the problem persists.', RegistrationStatus.INACTIVE, clientError);
107
107
  emitterCb(clientError);
108
108
  }
109
- export async function handleRegistrationErrors(err, emitterCb, loggerContext, retry429Cb, restoreRegCb) {
109
+ export async function handleRegistrationErrors(err, emitterCb, loggerContext, retry429Cb, restoreRegCb, serverCount = 1) {
110
+ let shouldDisconnect = false;
110
111
  const lineError = createLineError('', {}, ERROR_TYPE.DEFAULT, RegistrationStatus.INACTIVE);
111
112
  const errorCode = Number(err.statusCode);
112
113
  let finalError = false;
@@ -117,6 +118,7 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
117
118
  log.warn(`400 Bad Request`, loggerContext);
118
119
  updateLineErrorContext(loggerContext, ERROR_TYPE.BAD_REQUEST, 'Invalid input. Please verify the required parameters, sign out and then sign back in with the valid data', RegistrationStatus.INACTIVE, lineError);
119
120
  emitterCb(lineError, finalError);
121
+ shouldDisconnect = serverCount > 1;
120
122
  break;
121
123
  }
122
124
  case ERROR_CODE.UNAUTHORIZED: {
@@ -124,6 +126,7 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
124
126
  log.warn(`401 Unauthorized`, loggerContext);
125
127
  updateLineErrorContext(loggerContext, ERROR_TYPE.TOKEN_ERROR, 'User is unauthorized due to an expired token. Sign out, then sign back in.', RegistrationStatus.INACTIVE, lineError);
126
128
  emitterCb(lineError, finalError);
129
+ shouldDisconnect = serverCount > 1;
127
130
  break;
128
131
  }
129
132
  case ERROR_CODE.DEVICE_NOT_FOUND: {
@@ -131,6 +134,7 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
131
134
  log.warn(`404 Device Not Found`, loggerContext);
132
135
  updateLineErrorContext(loggerContext, ERROR_TYPE.NOT_FOUND, 'Webex Calling is unable to find your device. Sign out, then sign back in', RegistrationStatus.INACTIVE, lineError);
133
136
  emitterCb(lineError, finalError);
137
+ shouldDisconnect = serverCount > 1;
134
138
  break;
135
139
  }
136
140
  case ERROR_CODE.TOO_MANY_REQUESTS: {
@@ -139,20 +143,23 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
139
143
  const caller = loggerContext.method || 'handleErrors';
140
144
  if (retry429Cb && err.headers) {
141
145
  const retryAfter = Number(err.headers['retry-after']);
142
- retry429Cb(retryAfter, caller);
146
+ await retry429Cb(retryAfter, caller);
143
147
  }
148
+ shouldDisconnect = false;
144
149
  break;
145
150
  }
146
151
  case ERROR_CODE.INTERNAL_SERVER_ERROR: {
147
152
  log.warn(`500 Internal Server Error`, loggerContext);
148
153
  updateLineErrorContext(loggerContext, ERROR_TYPE.SERVER_ERROR, 'An unknown error occurred while placing the request. Wait a moment and try again.', RegistrationStatus.INACTIVE, lineError);
149
154
  emitterCb(lineError, finalError);
155
+ shouldDisconnect = serverCount > 1;
150
156
  break;
151
157
  }
152
158
  case ERROR_CODE.SERVICE_UNAVAILABLE: {
153
159
  log.warn(`503 Service Unavailable`, loggerContext);
154
160
  updateLineErrorContext(loggerContext, ERROR_TYPE.SERVICE_UNAVAILABLE, 'An error occurred on the server while processing the request. Wait a moment and try again.', RegistrationStatus.INACTIVE, lineError);
155
161
  emitterCb(lineError, finalError);
162
+ shouldDisconnect = serverCount > 1;
156
163
  break;
157
164
  }
158
165
  case ERROR_CODE.FORBIDDEN: {
@@ -162,7 +169,8 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
162
169
  log.warn('Error response has no body, throwing default error', loggerContext);
163
170
  updateLineErrorContext(loggerContext, ERROR_TYPE.FORBIDDEN_ERROR, 'An unauthorized action has been received. This action has been blocked. Please contact the administrator if this persists.', RegistrationStatus.INACTIVE, lineError);
164
171
  emitterCb(lineError, finalError);
165
- return finalError;
172
+ shouldDisconnect = serverCount > 1;
173
+ return { finalError, shouldDisconnect };
166
174
  }
167
175
  const code = Number(errorBody.errorCode);
168
176
  log.warn(`Error code found : ${code}`, loggerContext);
@@ -174,6 +182,7 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
174
182
  const caller = loggerContext.method || 'handleErrors';
175
183
  await restoreRegCb(errorBody, caller);
176
184
  }
185
+ shouldDisconnect = false;
177
186
  break;
178
187
  }
179
188
  case DEVICE_ERROR_CODE.DEVICE_CREATION_DISABLED: {
@@ -182,6 +191,7 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
182
191
  updateLineErrorContext(loggerContext, ERROR_TYPE.FORBIDDEN_ERROR, errorMessage, RegistrationStatus.INACTIVE, lineError);
183
192
  log.warn(errorMessage, loggerContext);
184
193
  emitterCb(lineError, true);
194
+ shouldDisconnect = true;
185
195
  break;
186
196
  }
187
197
  case DEVICE_ERROR_CODE.DEVICE_CREATION_FAILED: {
@@ -189,6 +199,7 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
189
199
  updateLineErrorContext(loggerContext, ERROR_TYPE.FORBIDDEN_ERROR, errorMessage, RegistrationStatus.INACTIVE, lineError);
190
200
  log.warn(errorMessage, loggerContext);
191
201
  emitterCb(lineError, finalError);
202
+ shouldDisconnect = serverCount > 1;
192
203
  break;
193
204
  }
194
205
  default: {
@@ -196,6 +207,7 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
196
207
  updateLineErrorContext(loggerContext, ERROR_TYPE.FORBIDDEN_ERROR, errorMessage, RegistrationStatus.INACTIVE, lineError);
197
208
  log.warn(errorMessage, loggerContext);
198
209
  emitterCb(lineError, finalError);
210
+ shouldDisconnect = serverCount > 1;
199
211
  }
200
212
  }
201
213
  break;
@@ -204,9 +216,10 @@ export async function handleRegistrationErrors(err, emitterCb, loggerContext, re
204
216
  updateLineErrorContext(loggerContext, ERROR_TYPE.DEFAULT, 'Unknown error', RegistrationStatus.INACTIVE, lineError);
205
217
  log.warn(`Unknown Error`, loggerContext);
206
218
  emitterCb(lineError, finalError);
219
+ shouldDisconnect = serverCount > 1;
207
220
  }
208
221
  }
209
- return finalError;
222
+ return { finalError, shouldDisconnect };
210
223
  }
211
224
  export async function handleCallingClientErrors(err, emitterCb, loggerContext) {
212
225
  const clientError = createClientError('', {}, ERROR_TYPE.DEFAULT, RegistrationStatus.INACTIVE);
@@ -40,7 +40,6 @@ export declare class CallingClient extends Eventing<CallingClientEventTypes> imp
40
40
  private getClientRegionInfo;
41
41
  private getMobiusServers;
42
42
  private connectToMobiusSocket;
43
- private registerMobiusSocketListener;
44
43
  private handleMobiusAsyncEvent;
45
44
  private registerCallsClearedListener;
46
45
  private callsClearedHandler;