@webex/internal-plugin-metrics 3.11.0-webex-services-ready.1 → 3.12.0-llmrefactor.1

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 (56) hide show
  1. package/dist/automated-user.js +19 -0
  2. package/dist/automated-user.js.map +1 -0
  3. package/dist/batcher.js +3 -0
  4. package/dist/batcher.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +639 -65
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  7. package/dist/call-diagnostic/call-diagnostic-metrics.js +168 -76
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  9. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +11 -3
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  11. package/dist/call-diagnostic/config.js +16 -3
  12. package/dist/call-diagnostic/config.js.map +1 -1
  13. package/dist/config.js +1 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/generic-metrics.js +8 -6
  16. package/dist/generic-metrics.js.map +1 -1
  17. package/dist/index.js +22 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/metrics.js +1 -1
  20. package/dist/metrics.types.js +4 -0
  21. package/dist/metrics.types.js.map +1 -1
  22. package/dist/new-metrics.js +69 -23
  23. package/dist/new-metrics.js.map +1 -1
  24. package/dist/prelogin-metrics.js +106 -0
  25. package/dist/prelogin-metrics.js.map +1 -0
  26. package/dist/types/automated-user.d.ts +2 -0
  27. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
  28. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +79 -15
  29. package/dist/types/call-diagnostic/config.d.ts +4 -0
  30. package/dist/types/config.d.ts +1 -0
  31. package/dist/types/index.d.ts +6 -3
  32. package/dist/types/metrics.types.d.ts +6 -4
  33. package/dist/types/new-metrics.d.ts +16 -0
  34. package/dist/types/prelogin-metrics.d.ts +47 -0
  35. package/package.json +12 -11
  36. package/src/automated-user.ts +16 -0
  37. package/src/batcher.js +4 -0
  38. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +800 -75
  39. package/src/call-diagnostic/call-diagnostic-metrics.ts +102 -15
  40. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +12 -2
  41. package/src/call-diagnostic/config.ts +14 -0
  42. package/src/config.js +1 -0
  43. package/src/generic-metrics.ts +6 -6
  44. package/src/index.ts +7 -0
  45. package/src/metrics.types.ts +18 -5
  46. package/src/new-metrics.ts +54 -4
  47. package/src/prelogin-metrics.ts +94 -0
  48. package/test/unit/spec/automated-user.ts +43 -0
  49. package/test/unit/spec/batcher.js +43 -0
  50. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +47 -11
  51. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1550 -416
  52. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +887 -159
  53. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +37 -3
  54. package/test/unit/spec/new-metrics.ts +39 -2
  55. package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
  56. package/test/unit/spec/prelogin-metrics.ts +132 -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,
@@ -99,7 +100,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
99
100
  // @ts-ignore
100
101
  private preLoginMetricsBatcher: PreLoginMetricsBatcher;
101
102
 
102
- private logger: any; // to avoid adding @ts-ignore everywhere
103
+ // lazy getter to avoid @ts-ignore on every call site
104
+ private get logger(): any {
105
+ // @ts-ignore
106
+ return this.webex.logger;
107
+ }
108
+
103
109
  private hasLoggedBrowserSerial: boolean;
104
110
  private device: any;
105
111
  private delayedClientEvents: DelayedClientEvent[] = [];
@@ -108,6 +114,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
108
114
  private isMercuryConnected = false;
109
115
  private eventLimitTracker: Map<string, number> = new Map();
110
116
  private eventLimitWarningsLogged: Set<string> = new Set();
117
+ private isTelemetryOptOutManual = false;
118
+ private isTelemetryOptOutAutomatic = false;
111
119
 
112
120
  // the default validator before piping an event to the batcher
113
121
  // this function can be overridden by the user
@@ -124,8 +132,6 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
124
132
  constructor(...args) {
125
133
  super(...args);
126
134
  // @ts-ignore
127
- this.logger = this.webex.logger;
128
- // @ts-ignore
129
135
  this.callDiagnosticEventsBatcher = new CallDiagnosticEventsBatcher({}, {parent: this.webex});
130
136
  // @ts-ignore
131
137
  this.preLoginMetricsBatcher = new PreLoginMetricsBatcher({}, {parent: this.webex});
@@ -145,6 +151,58 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
145
151
  return null;
146
152
  }
147
153
 
154
+ /**
155
+ * Returns the user activation state reported from the browser's navigator.userActivation API
156
+ * @returns object with hasBeenActive and isActive booleans, or undefined if unavailable
157
+ */
158
+ getUserActivation(): {hasBeenActive: boolean; isActive: boolean} | undefined {
159
+ const userActivation =
160
+ typeof navigator !== 'undefined'
161
+ ? (navigator as {userActivation?: {hasBeenActive: boolean; isActive: boolean}})
162
+ .userActivation
163
+ : undefined;
164
+
165
+ if (userActivation) {
166
+ return {
167
+ hasBeenActive: userActivation.hasBeenActive,
168
+ isActive: userActivation.isActive,
169
+ };
170
+ }
171
+
172
+ return undefined;
173
+ }
174
+
175
+ /**
176
+ * Returns the telemetryOptOut value of the current user
177
+ * @returns one of 'manual', 'automatic', undefined
178
+ */
179
+ public getTelemetryOptOut() {
180
+ if (this.isTelemetryOptOutManual) {
181
+ return 'manual';
182
+ }
183
+ if (this.isTelemetryOptOutAutomatic) {
184
+ return 'automatic';
185
+ }
186
+
187
+ return undefined;
188
+ }
189
+
190
+ /**
191
+ * Sets the manual telemetry opt-out status for the current user
192
+ * @param value - boolean value indicating manual telemetry opt-out status
193
+ */
194
+ public setIsTelemetryOptOutManual(value: boolean) {
195
+ this.isTelemetryOptOutManual = value;
196
+ }
197
+
198
+ /**
199
+ * Sets the automatic telemetry opt-out status for the current user
200
+ * @param value - boolean value indicating automatic telemetry opt-out status
201
+ */
202
+ public setIsTelemetryOptOutAutomatic(value: boolean) {
203
+ this.isTelemetryOptOutAutomatic = value;
204
+ }
205
+
148
206
  /**
149
207
  * Returns if the meeting has converged architecture enabled
150
208
  * @param options.meetingId
@@ -190,6 +248,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
190
248
 
191
249
  // if ConvergedArchitecture enable and isConvergedWebinarWebcast -- then webcast
192
250
  if (meetingInfo?.enableConvergedArchitecture && meetingInfo?.enableEvent) {
251
+ // if enableConvergedWebinarLargeScale - then large scale webinar
252
+ if (meetingInfo?.enableConvergedWebinarLargeScale) {
253
+ return WEBEX_SUB_SERVICE_TYPES.LARGE_SCALE_WEBINAR;
254
+ }
255
+
193
256
  return meetingInfo?.isConvergedWebinarWebcast
194
257
  ? WEBEX_SUB_SERVICE_TYPES.WEBCAST
195
258
  : WEBEX_SUB_SERVICE_TYPES.WEBINAR;
@@ -213,12 +276,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
213
276
  getOrigin(options: GetOriginOptions, meetingId?: string) {
214
277
  const defaultClientType: ClientType =
215
278
  // @ts-ignore
216
- this.webex.meetings.config?.metrics?.clientType;
279
+ this.webex.meetings?.config?.metrics?.clientType;
217
280
  const defaultSubClientType: SubClientType =
218
281
  // @ts-ignore
219
- this.webex.meetings.config?.metrics?.subClientType;
282
+ this.webex.meetings?.config?.metrics?.subClientType;
220
283
  // @ts-ignore
221
- const providedClientVersion: string = this.webex.meetings.config?.metrics?.clientVersion;
284
+ const providedClientVersion: string = this.webex.meetings?.config?.metrics?.clientVersion;
222
285
  // @ts-ignore
223
286
  const defaultSDKClientVersion = `${CLIENT_NAME}/${this.webex.version}`;
224
287
 
@@ -229,6 +292,18 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
229
292
  versionMetadata = extractVersionMetadata(providedClientVersion);
230
293
  }
231
294
 
295
+ // Browser details and the support verdict can all be supplied by the host client, which is able
296
+ // to resolve wrapper browsers (Electron, WebOS) to the engine that actually determines
297
+ // capability. The browser fields fall back to the SDK's own detection when not supplied, the
298
+ // same way clientVersion falls back to the SDK version.
299
+ const {
300
+ browser: providedBrowser,
301
+ browserVersion: providedBrowserVersion,
302
+ isSupportedBrowserFamily,
303
+ isOutdatedBrowserVersion,
304
+ // @ts-ignore
305
+ } = this.webex.meetings?.config?.metrics ?? {};
306
+
232
307
  if (!this.hasLoggedBrowserSerial) {
233
308
  this.logger.log(
234
309
  CALL_DIAGNOSTIC_LOG_IDENTIFIER,
@@ -258,25 +333,27 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
258
333
  ...versionMetadata,
259
334
  publicNetworkPrefix:
260
335
  // @ts-ignore
261
- anonymizeIPAddress(this.webex.meetings.geoHintInfo?.clientAddress) || undefined,
336
+ anonymizeIPAddress(this.webex.meetings?.geoHintInfo?.clientAddress) || undefined,
262
337
  localNetworkPrefix:
263
338
  anonymizeIPAddress(
264
339
  // @ts-ignore
265
- this.webex.meetings.meetingCollection
266
- .get(meetingId)
340
+ this.webex.meetings?.meetingCollection
341
+ ?.get(meetingId)
267
342
  ?.statsAnalyzer?.getLocalIpAddress()
268
343
  ) || undefined,
269
344
  osVersion: getOSVersion() || 'unknown',
270
345
  subClientType: options?.subClientType || defaultSubClientType,
271
346
  os: getOSNameInternal(),
272
- browser: getBrowserName(),
273
- browserVersion: getBrowserVersion(),
347
+ browser: providedBrowser || getBrowserName(),
348
+ browserVersion: providedBrowserVersion || getBrowserVersion(),
349
+ ...(isSupportedBrowserFamily === undefined ? {} : {isSupportedBrowserFamily}),
350
+ ...(isOutdatedBrowserVersion === undefined ? {} : {isOutdatedBrowserVersion}),
274
351
  },
275
352
  };
276
353
 
277
354
  if (meetingId) {
278
355
  // @ts-ignore
279
- const meeting = this.webex.meetings.getBasicMeetingInformation(meetingId);
356
+ const meeting = this.webex.meetings?.getBasicMeetingInformation(meetingId);
280
357
  if (meeting?.environment) {
281
358
  origin.environment = meeting.environment;
282
359
  }
@@ -366,6 +443,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
366
443
  if (meeting?.locusInfo?.fullState) {
367
444
  identifiers.locusUrl = meeting.locusUrl;
368
445
  identifiers.locusId = meeting.locusUrl && meeting.locusUrl.split('/').pop();
446
+ identifiers.locusSessionId = meeting.locusInfo.fullState.sessionId;
369
447
  identifiers.locusStartTime =
370
448
  meeting.locusInfo.fullState && meeting.locusInfo.fullState.lastActive;
371
449
  }
@@ -425,7 +503,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
425
503
  sent: 'not_defined_yet',
426
504
  },
427
505
  // @ts-ignore
428
- senderCountryCode: this.webex.meetings.geoHintInfo?.countryCode,
506
+ senderCountryCode: this.webex.meetings?.geoHintInfo?.countryCode,
429
507
  event: eventData,
430
508
  };
431
509
 
@@ -601,6 +679,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
601
679
  mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
602
680
  startTime: new Date().toISOString(),
603
681
  },
682
+ webexSubServiceType: this.getSubServiceType(meeting),
604
683
  };
605
684
 
606
685
  // merge any new properties, or override existing ones
@@ -983,7 +1062,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
983
1062
  sessionCorrelationId,
984
1063
  });
985
1064
 
986
- // create common event object structur
1065
+ // create common event object structure
987
1066
  const commonEventObject = {
988
1067
  name,
989
1068
  canProceed: true,
@@ -996,6 +1075,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
996
1075
  'loginType' in meeting.callStateForMetrics
997
1076
  ? meeting.callStateForMetrics.loginType
998
1077
  : this.getCurLoginType(),
1078
+ telemetryOptOut: this.getTelemetryOptOut(),
999
1079
  isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
1000
1080
  meetingId,
1001
1081
  }),
@@ -1004,6 +1084,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1004
1084
  webexSubServiceType: this.getSubServiceType(meeting),
1005
1085
  // @ts-ignore
1006
1086
  webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
1087
+ isVipMeeting: meeting?.meetingInfo?.vipmeeting || false,
1088
+ isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
1089
+ userActivation: this.getUserActivation(),
1007
1090
  };
1008
1091
 
1009
1092
  const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
@@ -1123,8 +1206,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1123
1206
  isMercuryConnected: this.isMercuryConnected,
1124
1207
  },
1125
1208
  loginType: this.getCurLoginType(),
1209
+ telemetryOptOut: this.getTelemetryOptOut(),
1126
1210
  // @ts-ignore
1127
1211
  webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
1212
+ isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
1213
+ userActivation: this.getUserActivation(),
1128
1214
  };
1129
1215
 
1130
1216
  if (options.joinFlowVersion) {
@@ -1334,6 +1420,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1334
1420
  eventPayload: event,
1335
1421
  type: ['diagnostic-event'],
1336
1422
  };
1423
+
1337
1424
  this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
1338
1425
 
1339
1426
  return this.preLoginMetricsBatcher.request(finalEvent);
@@ -1381,7 +1468,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
1381
1468
  },
1382
1469
  headers: {},
1383
1470
  // @ts-ignore
1384
- waitForServiceTimeout: this.webex.internal.metrics.config.waitForServiceTimeout,
1471
+ waitForServiceTimeout: this.webex.internal.metrics?.config?.waitForServiceTimeout,
1385
1472
  };
1386
1473
 
1387
1474
  if (options.preLoginId) {
@@ -284,7 +284,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
284
284
  devicePairingType: webex.devicemanager.getPairedMethod(),
285
285
  deviceURL: pairedDevice.url,
286
286
  isPersonalDevice: pairedDevice.mode === 'personal',
287
- productName: pairedDevice.devices[0]?.productName,
287
+ productName:
288
+ pairedDevice.devices?.length > 0 ? pairedDevice.devices[0]?.productName : undefined,
288
289
  };
289
290
  item.eventPayload.event.pairingState = 'paired';
290
291
  item.eventPayload.event.pairedDevice = devicePayload;
@@ -361,7 +362,6 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
361
362
  joinTimes.totalMediaJMT = cdl.getTotalMediaJMT();
362
363
  joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
363
364
  joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
364
- joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
365
365
  joinTimes.totalMediaJMTWithUserDelay = cdl.getTotalMediaJMTWithUserDelay();
366
366
  joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
367
367
  break;
@@ -369,6 +369,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
369
369
  case 'client.media.tx.start':
370
370
  audioSetupDelay.joinRespTxStart = cdl.getAudioJoinRespTxStart();
371
371
  videoSetupDelay.joinRespTxStart = cdl.getVideoJoinRespTxStart();
372
+ break;
373
+
374
+ case 'client.lobby.exited':
375
+ joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
376
+ break;
372
377
  }
373
378
 
374
379
  if (!isEmpty(joinTimes)) {
@@ -385,6 +390,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
385
390
 
386
391
  item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
387
392
 
393
+ // Mark call milestones in logs for easier filtering and analysis
394
+ if (eventName) {
395
+ webex.logger.log('Milestone,CallDiagnostic', eventName);
396
+ }
397
+
388
398
  webex.logger.log(
389
399
  `CallDiagnosticLatencies,prepareDiagnosticMetricItem: ${JSON.stringify({
390
400
  latencies: Object.fromEntries(cdl.latencyTimestamps),
@@ -26,6 +26,7 @@ export const WEBEX_SUB_SERVICE_TYPES: Record<string, ClientSubServiceType> = {
26
26
  SCHEDULED_MEETING: 'ScheduledMeeting',
27
27
  WEBINAR: 'Webinar',
28
28
  WEBCAST: 'Webcast',
29
+ LARGE_SCALE_WEBINAR: 'LargeScaleWebinar',
29
30
  };
30
31
 
31
32
  // Found in https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
@@ -136,6 +137,7 @@ export const ERROR_DESCRIPTIONS = {
136
137
  WEBRTC_API_NOT_AVAILABLE: 'WebrtcApiNotAvailableError',
137
138
  WDM_RESTRICTED_REGION: 'WdmRestrictedRegion',
138
139
  USER_NOT_ALLOWED_JOIN_WEBINAR: 'UserNotAllowedJoinWebinar',
140
+ INVALID_MEETING_INFO: 'InvalidMeetingInfo',
139
141
  };
140
142
 
141
143
  export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
@@ -146,6 +148,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
146
148
  99002: 4100,
147
149
  // Cannot find the data. Unkown meeting.
148
150
  99009: 4100,
151
+ // The input parameters contain invalid item
152
+ 99019: 4105,
149
153
  // Meeting is not allow to cross env
150
154
  58500: 4100,
151
155
  // Input parameters contain invalit item
@@ -201,6 +205,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
201
205
  423005: 4005,
202
206
  // Wrong password or host key with too many requests
203
207
  423006: 4005,
208
+ // PanelistPasswordError too many time,please input captcha code
209
+ 423008: 4005,
204
210
  // PasswordError with right captcha, please input captcha code
205
211
  423010: 4005,
206
212
  // PasswordOrHostKeyError with right captcha, please input captcha code
@@ -227,6 +233,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
227
233
  403003: 4101,
228
234
  // Attendee email is required
229
235
  403030: 4101,
236
+ // webinar need login when un-invited attendee join
237
+ 403106: 4104,
230
238
 
231
239
  // ---- Locus ------
232
240
  // FREE_USER_MAX_PARTICIPANTS_EXCEEDED
@@ -696,6 +704,12 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
696
704
  category: 'expected',
697
705
  fatal: true,
698
706
  },
707
+ 4105: {
708
+ errorDescription: ERROR_DESCRIPTIONS.INVALID_MEETING_INFO,
709
+ category: 'expected',
710
+ fatal: false,
711
+ shownToUser: true,
712
+ },
699
713
  2729: {
700
714
  errorDescription: ERROR_DESCRIPTIONS.NO_MEDIA_FOUND,
701
715
  category: 'expected',
package/src/config.js CHANGED
@@ -19,6 +19,7 @@ export default {
19
19
  batcherMaxCalls: 50,
20
20
  batcherMaxWait: 1500,
21
21
  batcherRetryPlateau: 32000,
22
+ batcherRetryOnNetworkError: true,
22
23
  waitForServiceTimeout: 30,
23
24
  },
24
25
  };
@@ -79,7 +79,7 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
79
79
  device: {
80
80
  id: this.getDeviceId(),
81
81
  },
82
- locale: window.navigator.language,
82
+ locale: window?.navigator.language,
83
83
  os: {
84
84
  name: getOSNameInternal(),
85
85
  version: getOSVersion(),
@@ -94,12 +94,12 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
94
94
  protected getBrowserDetails(): object {
95
95
  return {
96
96
  browser: getBrowserName(),
97
- browserHeight: window.innerHeight,
97
+ browserHeight: window?.innerHeight,
98
98
  browserVersion: getBrowserVersion(),
99
- browserWidth: window.innerWidth,
100
- domain: window.location.hostname,
101
- inIframe: window.self !== window.top,
102
- locale: window.navigator.language,
99
+ browserWidth: window?.innerWidth,
100
+ domain: window?.location.hostname,
101
+ inIframe: window?.self !== window?.top,
102
+ locale: window?.navigator.language,
103
103
  os: getOSNameInternal(),
104
104
  };
105
105
  }
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import Metrics from './metrics';
8
8
  import config from './config';
9
9
  import NewMetrics from './new-metrics';
10
10
  import * as Utils from './utils';
11
+ import * as AutomatedUserUtils from './automated-user';
11
12
  import {
12
13
  ClientEvent,
13
14
  ClientEventLeaveReason,
@@ -19,6 +20,7 @@ import {
19
20
  SubmitMQE,
20
21
  PreComputedLatencies,
21
22
  SubmitFeatureEvent,
23
+ LocusSyncLatencyEventName,
22
24
  } from './metrics.types';
23
25
  import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
24
26
  import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
@@ -28,6 +30,7 @@ import BehavioralMetrics from './behavioral-metrics';
28
30
  import OperationalMetrics from './operational-metrics';
29
31
  import BusinessMetrics from './business-metrics';
30
32
  import RtcMetrics from './rtcMetrics';
33
+ import PreLoginMetrics from './prelogin-metrics';
31
34
 
32
35
  registerInternalPlugin('metrics', Metrics, {
33
36
  config,
@@ -42,6 +45,7 @@ export {default, getOSNameInternal} from './metrics';
42
45
  export {
43
46
  config,
44
47
  CALL_DIAGNOSTIC_CONFIG,
48
+ AutomatedUserUtils,
45
49
  NewMetrics,
46
50
  Utils,
47
51
  CallDiagnosticUtils,
@@ -51,7 +55,9 @@ export {
51
55
  OperationalMetrics,
52
56
  BusinessMetrics,
53
57
  RtcMetrics,
58
+ PreLoginMetrics,
54
59
  };
60
+ export {isAutomatedUser, isAutomatedUserAgent} from './automated-user';
55
61
  export type {
56
62
  ClientEvent,
57
63
  ClientEventLeaveReason,
@@ -63,4 +69,5 @@ export type {
63
69
  SubmitBusinessEvent,
64
70
  PreComputedLatencies,
65
71
  SubmitFeatureEvent,
72
+ LocusSyncLatencyEventName,
66
73
  };
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  ClientEvent as RawClientEvent,
3
3
  Event as RawEvent,
4
4
  MediaQualityEvent as RawMediaQualityEvent,
@@ -149,6 +149,17 @@ export type SubmitMQEOptions = {
149
149
  globalMeetingId?: string;
150
150
  };
151
151
 
152
+ export const LOCUS_SYNC_LATENCY_EVENT_NAMES = [
153
+ 'internal.client.locus.sync.start',
154
+ 'internal.client.locus.hashtree.request',
155
+ 'internal.client.locus.hashtree.response',
156
+ 'internal.client.locus.sync.request',
157
+ 'internal.client.locus.sync.response',
158
+ 'internal.client.locus.sync.message.received',
159
+ ] as const;
160
+
161
+ export type LocusSyncLatencyEventName = (typeof LOCUS_SYNC_LATENCY_EVENT_NAMES)[number];
162
+
152
163
  export type InternalEvent = {
153
164
  name:
154
165
  | 'internal.client.meetinginfo.request'
@@ -156,14 +167,14 @@ export type InternalEvent = {
156
167
  | 'internal.register.device.request'
157
168
  | 'internal.register.device.response'
158
169
  | 'internal.reset.join.latencies'
159
- | 'internal.client.meeting.click.joinbutton'
160
170
  | 'internal.host.meeting.participant.admitted'
161
171
  | 'internal.client.meeting.interstitial-window.showed'
162
172
  | 'internal.client.interstitial-window.click.joinbutton'
163
173
  | 'internal.client.add-media.turn-discovery.start'
164
174
  | 'internal.client.add-media.turn-discovery.end'
165
175
  | 'internal.client.share.initiated'
166
- | 'internal.client.share.stopped';
176
+ | 'internal.client.share.stopped'
177
+ | LocusSyncLatencyEventName;
167
178
 
168
179
  payload?: never;
169
180
  options?: never;
@@ -197,7 +208,7 @@ export interface BusinessEventPayload {
197
208
  metricName: string;
198
209
  timestamp: number;
199
210
  context: DeviceContext;
200
- browserDetails: EventPayload;
211
+ browserDetails: object;
201
212
  value: EventPayload;
202
213
  }
203
214
 
@@ -319,12 +330,14 @@ export type PreComputedLatencies =
319
330
  | 'internal.get.cluster.time'
320
331
  | 'internal.click.to.interstitial'
321
332
  | 'internal.click.to.interstitial.with.user.delay'
333
+ | 'internal.click.to.interstitial.for.client.jmt'
322
334
  | 'internal.refresh.captcha.time'
323
335
  | 'internal.exchange.ci.token.time'
324
336
  | 'internal.get.u2c.time'
325
337
  | 'internal.call.init.join.req'
326
338
  | 'internal.other.app.api.time'
327
- | 'internal.api.fetch.intelligence.models';
339
+ | 'internal.api.fetch.intelligence.models'
340
+ | 'internal.client.locus.sync.random.backoff';
328
341
 
329
342
  export interface IdType {
330
343
  meetingId?: string;
@@ -9,6 +9,8 @@ import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
9
9
  import BehavioralMetrics from './behavioral-metrics';
10
10
  import OperationalMetrics from './operational-metrics';
11
11
  import BusinessMetrics from './business-metrics';
12
+ import PreLoginMetrics from './prelogin-metrics';
13
+ import PreLoginMetricsBatcher from './prelogin-metrics-batcher';
12
14
  import {
13
15
  RecursivePartial,
14
16
  MetricEventProduct,
@@ -28,6 +30,7 @@ import {
28
30
  import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
29
31
  import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
30
32
  import {generateCommonErrorMetadata} from './utils';
33
+ import {isAutomatedUser as detectAutomatedUser} from './automated-user';
31
34
 
32
35
  /**
33
36
  * Metrics plugin to centralize all types of metrics.
@@ -45,6 +48,7 @@ class Metrics extends WebexPlugin {
45
48
  behavioralMetrics: BehavioralMetrics;
46
49
  operationalMetrics: OperationalMetrics;
47
50
  businessMetrics: BusinessMetrics;
51
+ preLoginMetrics: PreLoginMetrics;
48
52
  isReady = false;
49
53
 
50
54
  /**
@@ -76,6 +80,8 @@ class Metrics extends WebexPlugin {
76
80
 
77
81
  // @ts-ignore
78
82
  this.callDiagnosticLatencies = new CallDiagnosticLatencies({}, {parent: this.webex});
83
+ // @ts-ignore
84
+ this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
79
85
  this.onReady();
80
86
  }
81
87
 
@@ -85,8 +91,13 @@ class Metrics extends WebexPlugin {
85
91
  private onReady() {
86
92
  // @ts-ignore
87
93
  this.webex.once('ready', () => {
88
- // @ts-ignore
89
- this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
94
+ this.preLoginMetrics = new PreLoginMetrics(
95
+ // @ts-ignore
96
+ new PreLoginMetricsBatcher({}, {parent: this.webex}),
97
+ {},
98
+ // @ts-ignore
99
+ {parent: this.webex}
100
+ );
90
101
  this.isReady = true;
91
102
  this.setDelaySubmitClientEvents({
92
103
  shouldDelay: this.delaySubmitClientEvents,
@@ -172,6 +183,13 @@ class Metrics extends WebexPlugin {
172
183
  return this.businessMetrics?.isReadyToSubmitEvents() ?? false;
173
184
  }
174
185
 
186
+ /**
187
+ * @returns whether the current user agent belongs to an automated user
188
+ */
189
+ isAutomatedUser() {
190
+ return detectAutomatedUser();
191
+ }
192
+
175
193
  /**
176
194
  * Behavioral event
177
195
  * @param args
@@ -251,6 +269,38 @@ class Metrics extends WebexPlugin {
251
269
  return this.businessMetrics.submitBusinessEvent({name, payload, table, metadata});
252
270
  }
253
271
 
272
+ /**
273
+ * Call Analyzer: Pre-Login Event
274
+ * @param args
275
+ */
276
+ submitPreLoginEvent({
277
+ name,
278
+ preLoginId,
279
+ payload,
280
+ metadata,
281
+ }: {
282
+ name: string;
283
+ preLoginId: string;
284
+ payload: EventPayload;
285
+ metadata?: EventPayload;
286
+ }): Promise<void> {
287
+ if (!this.isReady) {
288
+ // @ts-ignore
289
+ this.webex.logger.log(
290
+ `NewMetrics: @submitPreLoginEvent. Attempted to submit before webex.ready: ${name}`
291
+ );
292
+
293
+ return Promise.resolve();
294
+ }
295
+
296
+ return this.preLoginMetrics.submitPreLoginEvent({
297
+ name,
298
+ preLoginId,
299
+ payload,
300
+ metadata,
301
+ });
302
+ }
303
+
254
304
  /**
255
305
  * Call Analyzer: Media Quality Event
256
306
  * @param args
@@ -283,7 +333,7 @@ class Metrics extends WebexPlugin {
283
333
  payload?: RecursivePartial<FeatureEvent['payload']>;
284
334
  options: any;
285
335
  }) {
286
- if (!this.callDiagnosticLatencies || !this.callDiagnosticMetrics) {
336
+ if (!this.isReady) {
287
337
  // @ts-ignore
288
338
  this.webex.logger.log(
289
339
  `NewMetrics: @submitFeatureEvent. Attempted to submit before webex.ready. Event name: ${name}`
@@ -318,7 +368,7 @@ class Metrics extends WebexPlugin {
318
368
  payload?: RecursivePartial<ClientEvent['payload']>;
319
369
  options?: SubmitClientEventOptions;
320
370
  }): Promise<any> {
321
- if (!this.callDiagnosticLatencies || !this.callDiagnosticMetrics) {
371
+ if (!this.isReady) {
322
372
  // @ts-ignore
323
373
  this.webex.logger.log(
324
374
  `NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: ${name}`