@webex/internal-plugin-metrics 3.7.0-next.13 → 3.7.0-next.15

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.
@@ -18,6 +18,10 @@ declare class Metrics extends WebexPlugin {
18
18
  operationalMetrics: OperationalMetrics;
19
19
  businessMetrics: BusinessMetrics;
20
20
  isReady: boolean;
21
+ /**
22
+ * Whether or not to delay the submission of client events.
23
+ */
24
+ delaySubmitClientEvents: boolean;
21
25
  /**
22
26
  * Constructor
23
27
  * @param args
@@ -170,5 +174,12 @@ declare class Metrics extends WebexPlugin {
170
174
  * @returns {boolean}
171
175
  */
172
176
  isServiceErrorExpected(serviceErrorCode: number): boolean;
177
+ /**
178
+ * Sets the value of delaySubmitClientEvents. If set to true, client events will be delayed until submitDelayedClientEvents is called. If
179
+ * set to false, delayed client events will be submitted.
180
+ *
181
+ * @param {boolean} shouldDelay - A boolean value indicating whether to delay the submission of client events.
182
+ */
183
+ setDelaySubmitClientEvents(shouldDelay: boolean): Promise<any[]> | Promise<void>;
173
184
  }
174
185
  export default Metrics;
package/package.json CHANGED
@@ -26,22 +26,22 @@
26
26
  "@webex/eslint-config-legacy": "0.0.0",
27
27
  "@webex/jest-config-legacy": "0.0.0",
28
28
  "@webex/legacy-tools": "0.0.0",
29
- "@webex/test-helper-chai": "3.7.0-next.13",
30
- "@webex/test-helper-mocha": "3.7.0-next.13",
31
- "@webex/test-helper-mock-webex": "3.7.0-next.13",
32
- "@webex/test-helper-test-users": "3.7.0-next.13",
29
+ "@webex/test-helper-chai": "3.7.0-next.15",
30
+ "@webex/test-helper-mocha": "3.7.0-next.15",
31
+ "@webex/test-helper-mock-webex": "3.7.0-next.15",
32
+ "@webex/test-helper-test-users": "3.7.0-next.15",
33
33
  "eslint": "^8.24.0",
34
34
  "prettier": "^2.7.1",
35
35
  "sinon": "^9.2.4"
36
36
  },
37
37
  "dependencies": {
38
- "@webex/common": "3.7.0-next.13",
39
- "@webex/common-timers": "3.7.0-next.13",
38
+ "@webex/common": "3.7.0-next.15",
39
+ "@webex/common-timers": "3.7.0-next.15",
40
40
  "@webex/event-dictionary-ts": "^1.0.1643",
41
- "@webex/internal-plugin-metrics": "3.7.0-next.13",
42
- "@webex/test-helper-chai": "3.7.0-next.13",
43
- "@webex/test-helper-mock-webex": "3.7.0-next.13",
44
- "@webex/webex-core": "3.7.0-next.13",
41
+ "@webex/internal-plugin-metrics": "3.7.0-next.15",
42
+ "@webex/test-helper-chai": "3.7.0-next.15",
43
+ "@webex/test-helper-mock-webex": "3.7.0-next.15",
44
+ "@webex/webex-core": "3.7.0-next.15",
45
45
  "ip-anonymize": "^0.1.0",
46
46
  "lodash": "^4.17.21",
47
47
  "uuid": "^3.3.2"
@@ -54,5 +54,5 @@
54
54
  "test:style": "eslint ./src/**/*.*",
55
55
  "test:unit": "webex-legacy-tools test --unit --runner mocha"
56
56
  },
57
- "version": "3.7.0-next.13"
57
+ "version": "3.7.0-next.15"
58
58
  }
@@ -151,7 +151,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
151
151
  public getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames) {
152
152
  const start = this.latencyTimestamps.get(a);
153
153
  const end = this.latencyTimestamps.get(b);
154
- if (start && end) {
154
+
155
+ if (typeof start === 'number' && typeof end === 'number') {
155
156
  return end - start;
156
157
  }
157
158
 
@@ -193,7 +194,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
193
194
  public getU2CTime() {
194
195
  const u2cLatency = this.precomputedLatencies.get('internal.get.u2c.time');
195
196
 
196
- return u2cLatency ? Math.floor(u2cLatency) : undefined;
197
+ return typeof u2cLatency === 'number' ? Math.floor(u2cLatency) : undefined;
197
198
  }
198
199
 
199
200
  /**
@@ -296,7 +297,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
296
297
  * @returns - latency
297
298
  */
298
299
  public getPageJMT() {
299
- return this.precomputedLatencies.get('internal.client.pageJMT') || undefined;
300
+ const latency = this.precomputedLatencies.get('internal.client.pageJMT');
301
+
302
+ return typeof latency === 'number' ? latency : undefined;
300
303
  }
301
304
 
302
305
  /**
@@ -304,7 +307,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
304
307
  * @returns - latency
305
308
  */
306
309
  public getDownloadTimeJMT() {
307
- return this.precomputedLatencies.get('internal.download.time') || undefined;
310
+ const latency = this.precomputedLatencies.get('internal.download.time');
311
+
312
+ return typeof latency === 'number' ? latency : undefined;
308
313
  }
309
314
 
310
315
  /**
@@ -320,8 +325,15 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
320
325
  );
321
326
  }
322
327
 
323
- // for cross launch and guest flows
324
- return this.precomputedLatencies.get('internal.click.to.interstitial') || undefined;
328
+ const clickToInterstitialLatency = this.precomputedLatencies.get(
329
+ 'internal.click.to.interstitial'
330
+ );
331
+
332
+ if (typeof clickToInterstitialLatency === 'number') {
333
+ return clickToInterstitialLatency;
334
+ }
335
+
336
+ return undefined;
325
337
  }
326
338
 
327
339
  /**
@@ -358,7 +370,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
358
370
  // get the first timestamp
359
371
  const connectedMedia = this.latencyTimestamps.get('client.ice.end');
360
372
 
361
- const lobbyTime = this.getStayLobbyTime() || 0;
373
+ const lobbyTimeLatency = this.getStayLobbyTime();
374
+ const lobbyTime = typeof lobbyTimeLatency === 'number' ? lobbyTimeLatency : 0;
362
375
 
363
376
  if (interstitialJoinClickTimestamp && connectedMedia) {
364
377
  return connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
@@ -375,7 +388,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
375
388
  const clickToInterstitial = this.getClickToInterstitial();
376
389
  const interstitialToJoinOk = this.getInterstitialToJoinOK();
377
390
 
378
- if (clickToInterstitial && interstitialToJoinOk) {
391
+ if (typeof clickToInterstitial === 'number' && typeof interstitialToJoinOk === 'number') {
379
392
  return clickToInterstitial + interstitialToJoinOk;
380
393
  }
381
394
 
@@ -427,7 +440,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
427
440
  const interstitialToJoinOk = this.getInterstitialToJoinOK();
428
441
  const joinConfJMT = this.getJoinConfJMT();
429
442
 
430
- if (interstitialToJoinOk && joinConfJMT) {
443
+ if (typeof interstitialToJoinOk === 'number' && typeof joinConfJMT === 'number') {
431
444
  return interstitialToJoinOk - joinConfJMT;
432
445
  }
433
446
 
@@ -454,7 +467,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
454
467
  public getReachabilityClustersReqResp() {
455
468
  const reachablityClusterReqResp = this.precomputedLatencies.get('internal.get.cluster.time');
456
469
 
457
- return reachablityClusterReqResp ? Math.floor(reachablityClusterReqResp) : undefined;
470
+ return typeof reachablityClusterReqResp === 'number'
471
+ ? Math.floor(reachablityClusterReqResp)
472
+ : undefined;
458
473
  }
459
474
 
460
475
  /**
@@ -477,7 +492,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
477
492
  public getExchangeCITokenJMT() {
478
493
  const exchangeCITokenJMT = this.precomputedLatencies.get('internal.exchange.ci.token.time');
479
494
 
480
- return exchangeCITokenJMT ? Math.floor(exchangeCITokenJMT) : undefined;
495
+ return typeof exchangeCITokenJMT === 'number' ? Math.floor(exchangeCITokenJMT) : undefined;
481
496
  }
482
497
 
483
498
  /**
@@ -486,7 +501,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
486
501
  public getRefreshCaptchaReqResp() {
487
502
  const refreshCaptchaReqResp = this.precomputedLatencies.get('internal.refresh.captcha.time');
488
503
 
489
- return refreshCaptchaReqResp ? Math.floor(refreshCaptchaReqResp) : undefined;
504
+ return typeof refreshCaptchaReqResp === 'number'
505
+ ? Math.floor(refreshCaptchaReqResp)
506
+ : undefined;
490
507
  }
491
508
 
492
509
  /**
@@ -498,7 +515,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
498
515
  'internal.api.fetch.intelligence.models'
499
516
  );
500
517
 
501
- return downloadIntelligenceModelsReqResp
518
+ return typeof downloadIntelligenceModelsReqResp === 'number'
502
519
  ? Math.floor(downloadIntelligenceModelsReqResp)
503
520
  : undefined;
504
521
  }
@@ -40,6 +40,7 @@ import {
40
40
  ClientEventPayloadError,
41
41
  ClientSubServiceType,
42
42
  BrowserLaunchMethodType,
43
+ DelayedClientEvent,
43
44
  } from '../metrics.types';
44
45
  import CallDiagnosticEventsBatcher from './call-diagnostic-metrics-batcher';
45
46
  import PreLoginMetricsBatcher from '../prelogin-metrics-batcher';
@@ -95,6 +96,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
95
96
  private logger: any; // to avoid adding @ts-ignore everywhere
96
97
  private hasLoggedBrowserSerial: boolean;
97
98
  private device: any;
99
+ private delayedClientEvents: DelayedClientEvent[] = [];
98
100
 
99
101
  // the default validator before piping an event to the batcher
100
102
  // this function can be overridden by the user
@@ -730,6 +732,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
730
732
  meetingId,
731
733
  }),
732
734
  webexSubServiceType: this.getSubServiceType(meeting),
735
+ // @ts-ignore
736
+ webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
733
737
  };
734
738
 
735
739
  const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
@@ -782,6 +786,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
782
786
  webClientDomain: window.location.hostname,
783
787
  },
784
788
  loginType: this.getCurLoginType(),
789
+ // @ts-ignore
790
+ webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
785
791
  };
786
792
 
787
793
  if (options.joinFlowVersion) {
@@ -856,17 +862,36 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
856
862
  * @param arg.event - event key
857
863
  * @param arg.payload - additional payload to be merged with default payload
858
864
  * @param arg.options - payload
865
+ * @param arg.delaySubmitEvent - a boolean value indicating whether to delay the submission of client events.
859
866
  * @throws
860
867
  */
861
868
  public submitClientEvent({
862
869
  name,
863
870
  payload,
864
871
  options,
872
+ delaySubmitEvent,
865
873
  }: {
866
874
  name: ClientEvent['name'];
867
875
  payload?: ClientEventPayload;
868
876
  options?: SubmitClientEventOptions;
877
+ delaySubmitEvent?: boolean;
869
878
  }) {
879
+ if (delaySubmitEvent) {
880
+ // Preserve the time when the event was triggered if delaying the submission to Call Diagnostics
881
+ const delayedOptions = {
882
+ ...options,
883
+ triggeredTime: new Date().toISOString(),
884
+ };
885
+
886
+ this.delayedClientEvents.push({
887
+ name,
888
+ payload,
889
+ options: delayedOptions,
890
+ });
891
+
892
+ return Promise.resolve();
893
+ }
894
+
870
895
  this.logger.log(
871
896
  CALL_DIAGNOSTIC_LOG_IDENTIFIER,
872
897
  'CallDiagnosticMetrics: @submitClientEvent. Submit Client Event CA event.',
@@ -883,6 +908,28 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
883
908
  return this.submitToCallDiagnostics(diagnosticEvent);
884
909
  }
885
910
 
911
+ /**
912
+ * Submit Delayed Client Event CA events. Clears delayedClientEvents array after submission.
913
+ */
914
+ public submitDelayedClientEvents() {
915
+ this.logger.log(
916
+ CALL_DIAGNOSTIC_LOG_IDENTIFIER,
917
+ 'CallDiagnosticMetrics: @submitDelayedClientEvents. Submitting delayed client events.'
918
+ );
919
+
920
+ if (this.delayedClientEvents.length === 0) {
921
+ return Promise.resolve();
922
+ }
923
+
924
+ const promises = this.delayedClientEvents.map((delayedSubmitClientEventParams) => {
925
+ return this.submitClientEvent(delayedSubmitClientEventParams);
926
+ });
927
+
928
+ this.delayedClientEvents = [];
929
+
930
+ return Promise.all(promises);
931
+ }
932
+
886
933
  /**
887
934
  * Prepare the event and send the request to metrics-a service.
888
935
  * @param event
@@ -320,3 +320,9 @@ export interface IMetricsAttributes {
320
320
  meetingId?: string;
321
321
  callId?: string;
322
322
  }
323
+
324
+ export interface DelayedClientEvent {
325
+ name: ClientEvent['name'];
326
+ payload?: RecursivePartial<ClientEvent['payload']>;
327
+ options?: SubmitClientEventOptions;
328
+ }
@@ -45,6 +45,11 @@ class Metrics extends WebexPlugin {
45
45
  businessMetrics: BusinessMetrics;
46
46
  isReady = false;
47
47
 
48
+ /**
49
+ * Whether or not to delay the submission of client events.
50
+ */
51
+ delaySubmitClientEvents = false;
52
+
48
53
  /**
49
54
  * Constructor
50
55
  * @param args
@@ -290,7 +295,12 @@ class Metrics extends WebexPlugin {
290
295
  options: {meetingId: options?.meetingId},
291
296
  });
292
297
 
293
- return this.callDiagnosticMetrics.submitClientEvent({name, payload, options});
298
+ return this.callDiagnosticMetrics.submitClientEvent({
299
+ name,
300
+ payload,
301
+ options,
302
+ delaySubmitEvent: this.delaySubmitClientEvents,
303
+ });
294
304
  }
295
305
 
296
306
  /**
@@ -389,6 +399,22 @@ class Metrics extends WebexPlugin {
389
399
  public isServiceErrorExpected(serviceErrorCode: number): boolean {
390
400
  return this.callDiagnosticMetrics.isServiceErrorExpected(serviceErrorCode);
391
401
  }
402
+
403
+ /**
404
+ * Sets the value of delaySubmitClientEvents. If set to true, client events will be delayed until submitDelayedClientEvents is called. If
405
+ * set to false, delayed client events will be submitted.
406
+ *
407
+ * @param {boolean} shouldDelay - A boolean value indicating whether to delay the submission of client events.
408
+ */
409
+ public setDelaySubmitClientEvents(shouldDelay: boolean) {
410
+ this.delaySubmitClientEvents = shouldDelay;
411
+
412
+ if (!shouldDelay) {
413
+ return this.callDiagnosticMetrics.submitDelayedClientEvents();
414
+ }
415
+
416
+ return Promise.resolve();
417
+ }
392
418
  }
393
419
 
394
420
  export default Metrics;
@@ -437,6 +437,15 @@ describe('internal-plugin-metrics', () => {
437
437
  assert.deepEqual(cdl.getClickToInterstitial(), 5);
438
438
  });
439
439
 
440
+ it('calculates getClickToInterstitial without join button timestamp when it is 0', () => {
441
+ cdl.saveLatency('internal.click.to.interstitial', 0);
442
+ cdl.saveTimestamp({
443
+ key: 'internal.client.meeting.interstitial-window.showed',
444
+ value: 20,
445
+ });
446
+ assert.deepEqual(cdl.getClickToInterstitial(), 0);
447
+ });
448
+
440
449
  it('calculates getInterstitialToJoinOK correctly', () => {
441
450
  cdl.saveTimestamp({
442
451
  key: 'internal.client.interstitial-window.click.joinbutton',
@@ -449,6 +458,18 @@ describe('internal-plugin-metrics', () => {
449
458
  assert.deepEqual(cdl.getInterstitialToJoinOK(), 10);
450
459
  });
451
460
 
461
+ it('calculates getInterstitialToJoinOK correctly when one value is not a number', () => {
462
+ cdl.saveTimestamp({
463
+ key: 'internal.client.interstitial-window.click.joinbutton',
464
+ value: 'ten' as unknown as number,
465
+ });
466
+ cdl.saveTimestamp({
467
+ key: 'client.locus.join.response',
468
+ value: 20,
469
+ });
470
+ assert.deepEqual(cdl.getInterstitialToJoinOK(), undefined);
471
+ });
472
+
452
473
  it('calculates getCallInitMediaEngineReady correctly', () => {
453
474
  cdl.saveTimestamp({
454
475
  key: 'internal.client.interstitial-window.click.joinbutton',
@@ -481,6 +502,58 @@ describe('internal-plugin-metrics', () => {
481
502
  assert.deepEqual(cdl.getTotalJMT(), 45);
482
503
  });
483
504
 
505
+ it('calculates getTotalJMT correctly when clickToInterstitial is 0', () => {
506
+ cdl.saveLatency('internal.click.to.interstitial', 0);
507
+ cdl.saveTimestamp({
508
+ key: 'internal.client.interstitial-window.click.joinbutton',
509
+ value: 20,
510
+ });
511
+ cdl.saveTimestamp({
512
+ key: 'client.locus.join.response',
513
+ value: 40,
514
+ });
515
+ assert.deepEqual(cdl.getTotalJMT(), 20);
516
+ });
517
+
518
+ it('calculates getTotalJMT correctly when interstitialToJoinOk is 0', () => {
519
+ cdl.saveTimestamp({
520
+ key: 'internal.client.interstitial-window.click.joinbutton',
521
+ value: 40,
522
+ });
523
+ cdl.saveLatency('internal.click.to.interstitial', 12);
524
+ cdl.saveTimestamp({
525
+ key: 'client.locus.join.response',
526
+ value: 40,
527
+ });
528
+ assert.deepEqual(cdl.getTotalJMT(), 12);
529
+ });
530
+
531
+ it('calculates getTotalJMT correctly when both clickToInterstitial and interstitialToJoinOk are 0', () => {
532
+ cdl.saveTimestamp({
533
+ key: 'internal.client.interstitial-window.click.joinbutton',
534
+ value: 40,
535
+ });
536
+ cdl.saveLatency('internal.click.to.interstitial', 0);
537
+ cdl.saveTimestamp({
538
+ key: 'client.locus.join.response',
539
+ value: 40,
540
+ });
541
+ assert.deepEqual(cdl.getTotalJMT(), 0);
542
+ });
543
+
544
+ it('calculates getTotalJMT correctly when both clickToInterstitial is not a number', () => {
545
+ cdl.saveTimestamp({
546
+ key: 'internal.client.interstitial-window.click.joinbutton',
547
+ value: 40,
548
+ });
549
+ cdl.saveLatency('internal.click.to.interstitial', 'eleven' as unknown as number);
550
+ cdl.saveTimestamp({
551
+ key: 'client.locus.join.response',
552
+ value: 40,
553
+ });
554
+ assert.deepEqual(cdl.getTotalJMT(), undefined);
555
+ });
556
+
484
557
  it('calculates getTotalMediaJMT correctly', () => {
485
558
  cdl.saveTimestamp({
486
559
  key: 'internal.client.meeting.click.joinbutton',