@webex/internal-plugin-metrics 3.12.0-next.6 → 3.12.0-next.61

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 (98) hide show
  1. package/.repo-context.json +466 -0
  2. package/.sdd/manifest.json +626 -0
  3. package/AGENTS.md +208 -0
  4. package/README.md +274 -0
  5. package/dist/automated-user.js +19 -0
  6. package/dist/automated-user.js.map +1 -0
  7. package/dist/batcher.js +3 -0
  8. package/dist/batcher.js.map +1 -1
  9. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +613 -53
  10. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  11. package/dist/call-diagnostic/call-diagnostic-metrics.js +240 -103
  12. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  13. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
  14. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  15. package/dist/call-diagnostic/config.js +19 -4
  16. package/dist/call-diagnostic/config.js.map +1 -1
  17. package/dist/config.js +8 -0
  18. package/dist/config.js.map +1 -1
  19. package/dist/index.js +25 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/metrics.js +118 -5
  22. package/dist/metrics.js.map +1 -1
  23. package/dist/metrics.types.js +4 -0
  24. package/dist/metrics.types.js.map +1 -1
  25. package/dist/network-telemetry.js +610 -0
  26. package/dist/network-telemetry.js.map +1 -0
  27. package/dist/new-metrics.js +63 -17
  28. package/dist/new-metrics.js.map +1 -1
  29. package/dist/privacy-and-security-permission-enricher/constants.js +18 -0
  30. package/dist/privacy-and-security-permission-enricher/constants.js.map +1 -0
  31. package/dist/privacy-and-security-permission-enricher/index.js +143 -0
  32. package/dist/privacy-and-security-permission-enricher/index.js.map +1 -0
  33. package/dist/privacy-and-security-permission-enricher/types.js +7 -0
  34. package/dist/privacy-and-security-permission-enricher/types.js.map +1 -0
  35. package/dist/privacy-and-security-permission-enricher/utils.js +71 -0
  36. package/dist/privacy-and-security-permission-enricher/utils.js.map +1 -0
  37. package/dist/types/automated-user.d.ts +2 -0
  38. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +200 -5
  39. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +77 -17
  40. package/dist/types/call-diagnostic/config.d.ts +6 -0
  41. package/dist/types/config.d.ts +9 -0
  42. package/dist/types/index.d.ts +5 -3
  43. package/dist/types/metrics.types.d.ts +11 -5
  44. package/dist/types/network-telemetry.d.ts +108 -0
  45. package/dist/types/new-metrics.d.ts +17 -1
  46. package/dist/types/privacy-and-security-permission-enricher/constants.d.ts +6 -0
  47. package/dist/types/privacy-and-security-permission-enricher/index.d.ts +40 -0
  48. package/dist/types/privacy-and-security-permission-enricher/types.d.ts +14 -0
  49. package/dist/types/privacy-and-security-permission-enricher/utils.d.ts +5 -0
  50. package/dist/types/unhandled-exception-telemetry/index.d.ts +60 -0
  51. package/dist/types/unhandled-exception-telemetry/utils.d.ts +6 -0
  52. package/dist/unhandled-exception-telemetry/index.js +331 -0
  53. package/dist/unhandled-exception-telemetry/index.js.map +1 -0
  54. package/dist/unhandled-exception-telemetry/utils.js +105 -0
  55. package/dist/unhandled-exception-telemetry/utils.js.map +1 -0
  56. package/docs/adr/index.md +27 -0
  57. package/docs/architecture.md +437 -0
  58. package/docs/getting-started.md +131 -0
  59. package/docs/index.md +52 -0
  60. package/docs/specs/README.md +88 -0
  61. package/package.json +12 -11
  62. package/schemas/repo-context.schema.json +796 -0
  63. package/src/automated-user.ts +16 -0
  64. package/src/batcher.js +4 -0
  65. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +737 -60
  66. package/src/call-diagnostic/call-diagnostic-metrics.ts +184 -42
  67. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
  68. package/src/call-diagnostic/config.ts +16 -0
  69. package/src/call-diagnostic/docs/README.md +535 -0
  70. package/src/config.js +8 -0
  71. package/src/docs/README.md +703 -0
  72. package/src/index.ts +16 -0
  73. package/src/metrics.js +123 -5
  74. package/src/metrics.types.ts +29 -6
  75. package/src/network-telemetry.ts +757 -0
  76. package/src/new-metrics.ts +56 -6
  77. package/src/privacy-and-security-permission-enricher/constants.ts +33 -0
  78. package/src/privacy-and-security-permission-enricher/docs/README.md +356 -0
  79. package/src/privacy-and-security-permission-enricher/index.ts +142 -0
  80. package/src/privacy-and-security-permission-enricher/types.ts +21 -0
  81. package/src/privacy-and-security-permission-enricher/utils.ts +82 -0
  82. package/src/rtcMetrics/docs/README.md +447 -0
  83. package/src/unhandled-exception-telemetry/docs/README.md +536 -0
  84. package/src/unhandled-exception-telemetry/index.ts +404 -0
  85. package/src/unhandled-exception-telemetry/utils.ts +101 -0
  86. package/test/unit/spec/automated-user.ts +43 -0
  87. package/test/unit/spec/batcher.js +43 -0
  88. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +10 -1
  89. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1336 -131
  90. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +1265 -207
  91. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
  92. package/test/unit/spec/metrics.js +14 -5
  93. package/test/unit/spec/network-telemetry.ts +610 -0
  94. package/test/unit/spec/new-metrics.ts +203 -50
  95. package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
  96. package/test/unit/spec/privacy-and-security-permission-enricher.ts +318 -0
  97. package/test/unit/spec/unhandled-exception-telemetry/utils.ts +109 -0
  98. package/test/unit/spec/unhandled-exception-telemetry.ts +689 -0
@@ -6,6 +6,7 @@ import uuid from 'uuid';
6
6
  import {merge} from 'lodash';
7
7
  import {StatelessWebexPlugin} from '@webex/webex-core';
8
8
  import {getOSNameInternal} from '../metrics';
9
+ import * as AutomatedUserUtils from '../automated-user';
9
10
 
10
11
  import {
11
12
  anonymizeIPAddress,
@@ -63,10 +64,17 @@ import {
63
64
  SDP_OFFER_CREATION_ERROR_MAP,
64
65
  CALL_FEATURE_LOG_IDENTIFIER,
65
66
  CALL_FEATURE_EVENT_FAILED_TO_SEND,
67
+ LOCUS_RATE_LIMITED_OUTGOING_CLIENT_CODE,
68
+ LOCUS_UNAVAILABLE_CLIENT_CODE,
66
69
  } from './config';
67
70
 
68
71
  const {getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
69
72
 
73
+ const LOCUS_HTTP_STATUS_TO_CLIENT_ERROR_CODE: Partial<Record<number, number>> = {
74
+ 429: LOCUS_RATE_LIMITED_OUTGOING_CLIENT_CODE,
75
+ 503: LOCUS_UNAVAILABLE_CLIENT_CODE,
76
+ };
77
+
70
78
  type GetOriginOptions = {
71
79
  clientType: ClientType;
72
80
  subClientType: SubClientType;
@@ -86,6 +94,7 @@ type GetIdentifiersOptions = {
86
94
  preLoginId?: string;
87
95
  globalMeetingId?: string;
88
96
  webexConferenceIdStr?: string;
97
+ locusCluster?: string;
89
98
  };
90
99
 
91
100
  /**
@@ -99,7 +108,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
99
108
  // @ts-ignore
100
109
  private preLoginMetricsBatcher: PreLoginMetricsBatcher;
101
110
 
102
- private logger: any; // to avoid adding @ts-ignore everywhere
111
+ // lazy getter to avoid @ts-ignore on every call site
112
+ private get logger(): any {
113
+ // @ts-ignore
114
+ return this.webex.logger;
115
+ }
116
+
103
117
  private hasLoggedBrowserSerial: boolean;
104
118
  private device: any;
105
119
  private delayedClientEvents: DelayedClientEvent[] = [];
@@ -108,6 +122,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
108
122
  private isMercuryConnected = false;
109
123
  private eventLimitTracker: Map<string, number> = new Map();
110
124
  private eventLimitWarningsLogged: Set<string> = new Set();
125
+ private isTelemetryOptOutManual = false;
126
+ private isTelemetryOptOutAutomatic = false;
111
127
 
112
128
  // the default validator before piping an event to the batcher
113
129
  // this function can be overridden by the user
@@ -124,8 +140,6 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
124
140
  constructor(...args) {
125
141
  super(...args);
126
142
  // @ts-ignore
127
- this.logger = this.webex.logger;
128
- // @ts-ignore
129
143
  this.callDiagnosticEventsBatcher = new CallDiagnosticEventsBatcher({}, {parent: this.webex});
130
144
  // @ts-ignore
131
145
  this.preLoginMetricsBatcher = new PreLoginMetricsBatcher({}, {parent: this.webex});
@@ -145,6 +159,58 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
145
159
  return null;
146
160
  }
147
161
 
162
+ /**
163
+ * Returns the user activation state reported from the browser's navigator.userActivation API
164
+ * @returns object with hasBeenActive and isActive booleans, or undefined if unavailable
165
+ */
166
+ getUserActivation(): {hasBeenActive: boolean; isActive: boolean} | undefined {
167
+ const userActivation =
168
+ typeof navigator !== 'undefined'
169
+ ? (navigator as {userActivation?: {hasBeenActive: boolean; isActive: boolean}})
170
+ .userActivation
171
+ : undefined;
172
+
173
+ if (userActivation) {
174
+ return {
175
+ hasBeenActive: userActivation.hasBeenActive,
176
+ isActive: userActivation.isActive,
177
+ };
178
+ }
179
+
180
+ return undefined;
181
+ }
182
+
183
+ /**
184
+ * Returns the telemetryOptOut value of the current user
185
+ * @returns one of 'manual', 'automatic', undefined
186
+ */
187
+ public getTelemetryOptOut() {
188
+ if (this.isTelemetryOptOutManual) {
189
+ return 'manual';
190
+ }
191
+ if (this.isTelemetryOptOutAutomatic) {
192
+ return 'automatic';
193
+ }
194
+
195
+ return undefined;
196
+ }
197
+
198
+ /**
199
+ * Sets the manual telemetry opt-out status for the current user
200
+ * @param value - boolean value indicating manual telemetry opt-out status
201
+ */
202
+ public setIsTelemetryOptOutManual(value: boolean) {
203
+ this.isTelemetryOptOutManual = value;
204
+ }
205
+
206
+ /**
207
+ * Sets the automatic telemetry opt-out status for the current user
208
+ * @param value - boolean value indicating automatic telemetry opt-out status
209
+ */
210
+ public setIsTelemetryOptOutAutomatic(value: boolean) {
211
+ this.isTelemetryOptOutAutomatic = value;
212
+ }
213
+
148
214
  /**
149
215
  * Returns if the meeting has converged architecture enabled
150
216
  * @param options.meetingId
@@ -190,6 +256,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
190
256
 
191
257
  // if ConvergedArchitecture enable and isConvergedWebinarWebcast -- then webcast
192
258
  if (meetingInfo?.enableConvergedArchitecture && meetingInfo?.enableEvent) {
259
+ // if enableConvergedWebinarLargeScale - then large scale webinar
260
+ if (meetingInfo?.enableConvergedWebinarLargeScale) {
261
+ return WEBEX_SUB_SERVICE_TYPES.LARGE_SCALE_WEBINAR;
262
+ }
263
+
193
264
  return meetingInfo?.isConvergedWebinarWebcast
194
265
  ? WEBEX_SUB_SERVICE_TYPES.WEBCAST
195
266
  : WEBEX_SUB_SERVICE_TYPES.WEBINAR;
@@ -213,12 +284,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
213
284
  getOrigin(options: GetOriginOptions, meetingId?: string) {
214
285
  const defaultClientType: ClientType =
215
286
  // @ts-ignore
216
- this.webex.meetings.config?.metrics?.clientType;
287
+ this.webex.meetings?.config?.metrics?.clientType;
217
288
  const defaultSubClientType: SubClientType =
218
289
  // @ts-ignore
219
- this.webex.meetings.config?.metrics?.subClientType;
290
+ this.webex.meetings?.config?.metrics?.subClientType;
220
291
  // @ts-ignore
221
- const providedClientVersion: string = this.webex.meetings.config?.metrics?.clientVersion;
292
+ const providedClientVersion: string = this.webex.meetings?.config?.metrics?.clientVersion;
222
293
  // @ts-ignore
223
294
  const defaultSDKClientVersion = `${CLIENT_NAME}/${this.webex.version}`;
224
295
 
@@ -229,6 +300,18 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
229
300
  versionMetadata = extractVersionMetadata(providedClientVersion);
230
301
  }
231
302
 
303
+ // Browser details and the support verdict can all be supplied by the host client, which is able
304
+ // to resolve wrapper browsers (Electron, WebOS) to the engine that actually determines
305
+ // capability. The browser fields fall back to the SDK's own detection when not supplied, the
306
+ // same way clientVersion falls back to the SDK version.
307
+ const {
308
+ browser: providedBrowser,
309
+ browserVersion: providedBrowserVersion,
310
+ isSupportedBrowserFamily,
311
+ isOutdatedBrowserVersion,
312
+ // @ts-ignore
313
+ } = this.webex.meetings?.config?.metrics ?? {};
314
+
232
315
  if (!this.hasLoggedBrowserSerial) {
233
316
  this.logger.log(
234
317
  CALL_DIAGNOSTIC_LOG_IDENTIFIER,
@@ -258,25 +341,27 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
258
341
  ...versionMetadata,
259
342
  publicNetworkPrefix:
260
343
  // @ts-ignore
261
- anonymizeIPAddress(this.webex.meetings.geoHintInfo?.clientAddress) || undefined,
344
+ anonymizeIPAddress(this.webex.meetings?.geoHintInfo?.clientAddress) || undefined,
262
345
  localNetworkPrefix:
263
346
  anonymizeIPAddress(
264
347
  // @ts-ignore
265
- this.webex.meetings.meetingCollection
266
- .get(meetingId)
348
+ this.webex.meetings?.meetingCollection
349
+ ?.get(meetingId)
267
350
  ?.statsAnalyzer?.getLocalIpAddress()
268
351
  ) || undefined,
269
352
  osVersion: getOSVersion() || 'unknown',
270
353
  subClientType: options?.subClientType || defaultSubClientType,
271
354
  os: getOSNameInternal(),
272
- browser: getBrowserName(),
273
- browserVersion: getBrowserVersion(),
355
+ browser: providedBrowser || getBrowserName(),
356
+ browserVersion: providedBrowserVersion || getBrowserVersion(),
357
+ ...(isSupportedBrowserFamily === undefined ? {} : {isSupportedBrowserFamily}),
358
+ ...(isOutdatedBrowserVersion === undefined ? {} : {isOutdatedBrowserVersion}),
274
359
  },
275
360
  };
276
361
 
277
362
  if (meetingId) {
278
363
  // @ts-ignore
279
- const meeting = this.webex.meetings.getBasicMeetingInformation(meetingId);
364
+ const meeting = this.webex.meetings?.getBasicMeetingInformation(meetingId);
280
365
  if (meeting?.environment) {
281
366
  origin.environment = meeting.environment;
282
367
  }
@@ -308,6 +393,20 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
308
393
  throw new Error("ClientType and SubClientType can't be undefined");
309
394
  }
310
395
 
396
+ /**
397
+ * Returns the signed-in user's CI ID derived from credentials tokens.
398
+ * Used as a fallback when no device is registered.
399
+ * @returns the userId, or undefined if it can't be determined
400
+ */
401
+ private getUserIdFromCredentials(): string | undefined {
402
+ try {
403
+ // @ts-ignore
404
+ return this.webex.credentials.getUserId();
405
+ } catch {
406
+ return undefined;
407
+ }
408
+ }
409
+
311
410
  /**
312
411
  * Gather identifier details for call diagnostic payload.
313
412
  * @throws Error if initialization fails.
@@ -322,6 +421,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
322
421
  globalMeetingId,
323
422
  preLoginId,
324
423
  sessionCorrelationId,
424
+ locusCluster,
325
425
  } = options;
326
426
  const identifiers: Event['event']['identifiers'] = {
327
427
  correlationId: 'unknown', // concerned with setting this to unknown. This will fail diagnostic events parsing because it's not a uuid pattern
@@ -352,7 +452,6 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
352
452
  const {device} = this;
353
453
  const {installationId} = device?.config || {};
354
454
 
355
- identifiers.userId = device?.userId || preLoginId;
356
455
  identifiers.deviceId = device?.url;
357
456
  identifiers.orgId = device?.orgId;
358
457
  // @ts-ignore
@@ -363,6 +462,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
363
462
  }
364
463
  }
365
464
 
465
+ // Prefer the device's userId, but fall back to the signed-in user's ID from
466
+ // credentials when no device is registered, then to the pre-login ID.
467
+ identifiers.userId = this.device?.userId || this.getUserIdFromCredentials() || preLoginId;
468
+
366
469
  if (meeting?.locusInfo?.fullState) {
367
470
  identifiers.locusUrl = meeting.locusUrl;
368
471
  identifiers.locusId = meeting.locusUrl && meeting.locusUrl.split('/').pop();
@@ -385,6 +488,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
385
488
  identifiers.webexSiteName = meeting.meetingInfo?.siteName;
386
489
  }
387
490
 
491
+ if (meeting?.meetingInfo?.locusClusterUrl) {
492
+ identifiers.locusCluster = meeting.meetingInfo.locusClusterUrl;
493
+ }
494
+
388
495
  if (mediaConnections) {
389
496
  identifiers.mediaAgentAlias = mediaConnections?.[0]?.mediaAgentAlias;
390
497
  identifiers.mediaAgentGroupId = mediaConnections?.[0]?.mediaAgentGroupId;
@@ -398,6 +505,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
398
505
  identifiers.globalMeetingId = globalMeetingId;
399
506
  }
400
507
 
508
+ if (!identifiers?.locusCluster && locusCluster) {
509
+ identifiers.locusCluster = locusCluster;
510
+ }
511
+
401
512
  if (identifiers.correlationId === undefined) {
402
513
  throw new Error('Identifiers initialization failed.');
403
514
  }
@@ -426,7 +537,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
426
537
  sent: 'not_defined_yet',
427
538
  },
428
539
  // @ts-ignore
429
- senderCountryCode: this.webex.meetings.geoHintInfo?.countryCode,
540
+ senderCountryCode: this.webex.meetings?.geoHintInfo?.countryCode,
430
541
  event: eventData,
431
542
  };
432
543
 
@@ -602,6 +713,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
602
713
  mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
603
714
  startTime: new Date().toISOString(),
604
715
  },
716
+ webexSubServiceType: this.getSubServiceType(meeting),
605
717
  };
606
718
 
607
719
  // merge any new properties, or override existing ones
@@ -624,7 +736,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
624
736
  * @param arg.clientErrorCode
625
737
  * @param arg.serviceErrorCode
626
738
  * @param arg.payloadOverrides
627
- * @param arg.httpStatusCode
739
+ * @param arg.httpCode
628
740
  * @returns
629
741
  */
630
742
  public getErrorPayloadForClientErrorCode({
@@ -633,14 +745,14 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
633
745
  serviceErrorName,
634
746
  rawErrorMessage,
635
747
  payloadOverrides,
636
- httpStatusCode,
748
+ httpCode,
637
749
  }: {
638
750
  clientErrorCode: number;
639
751
  serviceErrorCode: any;
640
752
  serviceErrorName?: any;
641
753
  rawErrorMessage?: string;
642
754
  payloadOverrides?: any;
643
- httpStatusCode?: number;
755
+ httpCode?: number;
644
756
  }): ClientEventError {
645
757
  let error: ClientEventError;
646
758
 
@@ -654,7 +766,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
654
766
  serviceErrorName ? {errorData: {errorName: serviceErrorName}} : {},
655
767
  {serviceErrorCode},
656
768
  {rawErrorMessage},
657
- httpStatusCode === undefined ? {} : {httpStatusCode},
769
+ httpCode === undefined ? {} : {httpCode},
658
770
  partialParsedError,
659
771
  payloadOverrides || {}
660
772
  );
@@ -823,7 +935,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
823
935
  }
824
936
 
825
937
  const rawErrorMessage = rawError.message;
826
- const httpStatusCode = rawError.statusCode;
938
+ const httpCode = rawError.statusCode;
939
+ const serviceErrorCode =
940
+ rawError?.error?.body?.errorCode ||
941
+ rawError?.body?.errorCode ||
942
+ rawError?.body?.code ||
943
+ rawError?.body?.reason?.reasonCode;
827
944
  let payload;
828
945
 
829
946
  if (rawError.name) {
@@ -833,7 +950,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
833
950
  clientErrorCode: BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP[rawError.name],
834
951
  serviceErrorName: rawError.name,
835
952
  rawErrorMessage,
836
- httpStatusCode,
953
+ httpCode,
837
954
  });
838
955
  }
839
956
  }
@@ -848,16 +965,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
848
965
  SDP_OFFER_CREATION_ERROR_MAP[causeType] || SDP_OFFER_CREATION_ERROR_MAP.GENERAL,
849
966
  serviceErrorName: rawError.name,
850
967
  rawErrorMessage,
851
- httpStatusCode,
968
+ httpCode,
852
969
  });
853
970
  }
854
971
 
855
- const serviceErrorCode =
856
- rawError?.error?.body?.errorCode ||
857
- rawError?.body?.errorCode ||
858
- rawError?.body?.code ||
859
- rawError?.body?.reason?.reasonCode;
860
-
861
972
  if (serviceErrorCode) {
862
973
  const clientErrorCode = SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP[serviceErrorCode];
863
974
  if (clientErrorCode && !payload) {
@@ -865,17 +976,30 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
865
976
  clientErrorCode,
866
977
  serviceErrorCode,
867
978
  rawErrorMessage,
868
- httpStatusCode,
979
+ httpCode,
869
980
  });
870
981
  }
982
+ }
871
983
 
984
+ const locusHttpClientErrorCode = LOCUS_HTTP_STATUS_TO_CLIENT_ERROR_CODE[httpCode];
985
+ const requestUrl = rawError.options?.uri || rawError.options?.url;
986
+ if (locusHttpClientErrorCode && !payload && requestUrl?.includes('locus')) {
987
+ payload = this.getErrorPayloadForClientErrorCode({
988
+ clientErrorCode: locusHttpClientErrorCode,
989
+ serviceErrorCode,
990
+ rawErrorMessage,
991
+ httpCode,
992
+ });
993
+ }
994
+
995
+ if (serviceErrorCode) {
872
996
  // by default, if it is locus error, return new locus err
873
997
  if (isLocusServiceErrorCode(serviceErrorCode) && !payload) {
874
998
  payload = this.getErrorPayloadForClientErrorCode({
875
999
  clientErrorCode: NEW_LOCUS_ERROR_CLIENT_CODE,
876
1000
  serviceErrorCode,
877
1001
  rawErrorMessage,
878
- httpStatusCode,
1002
+ httpCode,
879
1003
  });
880
1004
  }
881
1005
  }
@@ -885,7 +1009,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
885
1009
  clientErrorCode: MEETING_INFO_LOOKUP_ERROR_CLIENT_CODE,
886
1010
  serviceErrorCode,
887
1011
  rawErrorMessage,
888
- httpStatusCode,
1012
+ httpCode,
889
1013
  });
890
1014
  }
891
1015
 
@@ -895,7 +1019,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
895
1019
  serviceErrorCode,
896
1020
  payloadOverrides: rawError.payloadOverrides,
897
1021
  rawErrorMessage,
898
- httpStatusCode,
1022
+ httpCode,
899
1023
  });
900
1024
  }
901
1025
 
@@ -905,7 +1029,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
905
1029
  serviceErrorCode,
906
1030
  payloadOverrides: rawError.payloadOverrides,
907
1031
  rawErrorMessage,
908
- httpStatusCode,
1032
+ httpCode,
909
1033
  });
910
1034
  }
911
1035
 
@@ -917,7 +1041,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
917
1041
  serviceErrorName: rawError?.name,
918
1042
  payloadOverrides: rawError.payloadOverrides,
919
1043
  rawErrorMessage,
920
- httpStatusCode,
1044
+ httpCode,
921
1045
  });
922
1046
  }
923
1047
 
@@ -949,6 +1073,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
949
1073
  globalMeetingId,
950
1074
  webexConferenceIdStr,
951
1075
  sessionCorrelationId,
1076
+ locusCluster,
952
1077
  } = options;
953
1078
 
954
1079
  // @ts-ignore
@@ -982,9 +1107,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
982
1107
  webexConferenceIdStr,
983
1108
  globalMeetingId,
984
1109
  sessionCorrelationId,
1110
+ locusCluster,
985
1111
  });
986
1112
 
987
- // create common event object structur
1113
+ // create common event object structure
988
1114
  const commonEventObject = {
989
1115
  name,
990
1116
  canProceed: true,
@@ -997,6 +1123,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
997
1123
  'loginType' in meeting.callStateForMetrics
998
1124
  ? meeting.callStateForMetrics.loginType
999
1125
  : this.getCurLoginType(),
1126
+ telemetryOptOut: this.getTelemetryOptOut(),
1000
1127
  isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
1001
1128
  meetingId,
1002
1129
  }),
@@ -1005,6 +1132,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1005
1132
  webexSubServiceType: this.getSubServiceType(meeting),
1006
1133
  // @ts-ignore
1007
1134
  webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
1135
+ isVipMeeting: meeting?.meetingInfo?.vipmeeting || false,
1136
+ isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
1137
+ userActivation: this.getUserActivation(),
1008
1138
  };
1009
1139
 
1010
1140
  const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
@@ -1018,9 +1148,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1018
1148
  commonEventObject.meetingJoinedTime = meetingJoinedTime;
1019
1149
  }
1020
1150
 
1021
- if (options.meetingJoinPhase) {
1151
+ const meetingJoinPhase =
1152
+ options.meetingJoinPhase ?? meeting.callStateForMetrics?.meetingJoinPhase;
1153
+ if (meetingJoinPhase) {
1022
1154
  // @ts-ignore
1023
- commonEventObject.meetingJoinPhase = options.meetingJoinPhase;
1155
+ commonEventObject.meetingJoinPhase = meetingJoinPhase;
1024
1156
  }
1025
1157
 
1026
1158
  return commonEventObject;
@@ -1101,8 +1233,14 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1101
1233
  options?: SubmitClientEventOptions;
1102
1234
  errors?: ClientEventPayloadError;
1103
1235
  }) {
1104
- const {correlationId, globalMeetingId, webexConferenceIdStr, preLoginId, sessionCorrelationId} =
1105
- options;
1236
+ const {
1237
+ correlationId,
1238
+ globalMeetingId,
1239
+ webexConferenceIdStr,
1240
+ preLoginId,
1241
+ sessionCorrelationId,
1242
+ locusCluster,
1243
+ } = options;
1106
1244
 
1107
1245
  // grab identifiers
1108
1246
  const identifiers = this.getIdentifiers({
@@ -1111,6 +1249,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1111
1249
  preLoginId,
1112
1250
  globalMeetingId,
1113
1251
  webexConferenceIdStr,
1252
+ locusCluster,
1114
1253
  });
1115
1254
 
1116
1255
  // create client event object
@@ -1124,17 +1263,19 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1124
1263
  isMercuryConnected: this.isMercuryConnected,
1125
1264
  },
1126
1265
  loginType: this.getCurLoginType(),
1266
+ telemetryOptOut: this.getTelemetryOptOut(),
1127
1267
  // @ts-ignore
1128
1268
  webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
1269
+ isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
1270
+ userActivation: this.getUserActivation(),
1129
1271
  };
1130
1272
 
1131
1273
  if (options.joinFlowVersion) {
1132
1274
  clientEventObject.joinFlowVersion = options.joinFlowVersion;
1133
1275
  }
1134
1276
 
1135
- if (options.meetingJoinPhase) {
1136
- clientEventObject.meetingJoinPhase = options.meetingJoinPhase;
1137
- }
1277
+ // No meeting exists yet for pre-meeting events, so the join phase is pre-join by construction
1278
+ clientEventObject.meetingJoinPhase = options.meetingJoinPhase ?? 'pre-join';
1138
1279
 
1139
1280
  if (options.userNameInput) {
1140
1281
  clientEventObject.userNameInput = options.userNameInput;
@@ -1335,6 +1476,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1335
1476
  eventPayload: event,
1336
1477
  type: ['diagnostic-event'],
1337
1478
  };
1479
+
1338
1480
  this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
1339
1481
 
1340
1482
  return this.preLoginMetricsBatcher.request(finalEvent);
@@ -1382,7 +1524,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1382
1524
  },
1383
1525
  headers: {},
1384
1526
  // @ts-ignore
1385
- waitForServiceTimeout: this.webex.internal.metrics.config.waitForServiceTimeout,
1527
+ waitForServiceTimeout: this.webex.internal.metrics?.config?.waitForServiceTimeout,
1386
1528
  };
1387
1529
 
1388
1530
  if (options.preLoginId) {
@@ -390,6 +390,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
390
390
 
391
391
  item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
392
392
 
393
+ // Mark call milestones in logs for easier filtering and analysis
394
+ if (eventName) {
395
+ webex.logger.log('Milestone,CallDiagnostic', eventName);
396
+ }
397
+
393
398
  webex.logger.log(
394
399
  `CallDiagnosticLatencies,prepareDiagnosticMetricItem: ${JSON.stringify({
395
400
  latencies: Object.fromEntries(cdl.latencyTimestamps),
@@ -8,6 +8,8 @@ export const CALL_DIAGNOSTIC_LOG_IDENTIFIER = 'call-diagnostic-events -> ';
8
8
  export const CALL_FEATURE_LOG_IDENTIFIER = 'call-diagnostic-events-feature -> ';
9
9
 
10
10
  export const AUTHENTICATION_FAILED_CODE = 1010;
11
+ export const LOCUS_RATE_LIMITED_OUTGOING_CLIENT_CODE = 1002;
12
+ export const LOCUS_UNAVAILABLE_CLIENT_CODE = 1003;
11
13
  export const NETWORK_ERROR = 1026;
12
14
  export const NEW_LOCUS_ERROR_CLIENT_CODE = 4008;
13
15
  export const MEETING_INFO_LOOKUP_ERROR_CLIENT_CODE = 4100;
@@ -26,6 +28,7 @@ export const WEBEX_SUB_SERVICE_TYPES: Record<string, ClientSubServiceType> = {
26
28
  SCHEDULED_MEETING: 'ScheduledMeeting',
27
29
  WEBINAR: 'Webinar',
28
30
  WEBCAST: 'Webcast',
31
+ LARGE_SCALE_WEBINAR: 'LargeScaleWebinar',
29
32
  };
30
33
 
31
34
  // Found in https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
@@ -136,6 +139,7 @@ export const ERROR_DESCRIPTIONS = {
136
139
  WEBRTC_API_NOT_AVAILABLE: 'WebrtcApiNotAvailableError',
137
140
  WDM_RESTRICTED_REGION: 'WdmRestrictedRegion',
138
141
  USER_NOT_ALLOWED_JOIN_WEBINAR: 'UserNotAllowedJoinWebinar',
142
+ INVALID_MEETING_INFO: 'InvalidMeetingInfo',
139
143
  };
140
144
 
141
145
  export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
@@ -146,6 +150,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
146
150
  99002: 4100,
147
151
  // Cannot find the data. Unkown meeting.
148
152
  99009: 4100,
153
+ // The input parameters contain invalid item
154
+ 99019: 4105,
149
155
  // Meeting is not allow to cross env
150
156
  58500: 4100,
151
157
  // Input parameters contain invalit item
@@ -201,6 +207,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
201
207
  423005: 4005,
202
208
  // Wrong password or host key with too many requests
203
209
  423006: 4005,
210
+ // PanelistPasswordError too many time,please input captcha code
211
+ 423008: 4005,
204
212
  // PasswordError with right captcha, please input captcha code
205
213
  423010: 4005,
206
214
  // PasswordOrHostKeyError with right captcha, please input captcha code
@@ -227,6 +235,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
227
235
  403003: 4101,
228
236
  // Attendee email is required
229
237
  403030: 4101,
238
+ // webinar need login when un-invited attendee join
239
+ 403106: 4104,
230
240
 
231
241
  // ---- Locus ------
232
242
  // FREE_USER_MAX_PARTICIPANTS_EXCEEDED
@@ -696,6 +706,12 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
696
706
  category: 'expected',
697
707
  fatal: true,
698
708
  },
709
+ 4105: {
710
+ errorDescription: ERROR_DESCRIPTIONS.INVALID_MEETING_INFO,
711
+ category: 'expected',
712
+ fatal: false,
713
+ shownToUser: true,
714
+ },
699
715
  2729: {
700
716
  errorDescription: ERROR_DESCRIPTIONS.NO_MEDIA_FOUND,
701
717
  category: 'expected',