@webex/internal-plugin-metrics 3.8.1 → 3.9.0-webinar5k.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 (35) hide show
  1. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +49 -0
  2. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics.js +218 -48
  4. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +6 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  7. package/dist/call-diagnostic/config.js +3 -1
  8. package/dist/call-diagnostic/config.js.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/metrics.js +1 -1
  11. package/dist/metrics.types.js.map +1 -1
  12. package/dist/new-metrics.js +43 -1
  13. package/dist/new-metrics.js.map +1 -1
  14. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +15 -0
  15. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +144 -9
  16. package/dist/types/call-diagnostic/config.d.ts +2 -0
  17. package/dist/types/index.d.ts +2 -2
  18. package/dist/types/metrics.types.d.ts +17 -6
  19. package/dist/types/new-metrics.d.ts +19 -2
  20. package/package.json +12 -12
  21. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +58 -0
  22. package/src/call-diagnostic/call-diagnostic-metrics.ts +219 -25
  23. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +6 -0
  24. package/src/call-diagnostic/config.ts +3 -0
  25. package/src/index.ts +2 -0
  26. package/src/metrics.types.ts +22 -5
  27. package/src/new-metrics.ts +52 -1
  28. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +20 -1
  29. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +167 -0
  30. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +425 -38
  31. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +6 -0
  32. package/test/unit/spec/new-metrics.ts +67 -2
  33. package/test/unit/spec/prelogin-metrics-batcher.ts +1 -0
  34. package/dist/call-diagnostic-events-batcher.js +0 -60
  35. package/dist/call-diagnostic-events-batcher.js.map +0 -1
@@ -41,6 +41,9 @@ import {
41
41
  ClientSubServiceType,
42
42
  BrowserLaunchMethodType,
43
43
  DelayedClientEvent,
44
+ DelayedClientFeatureEvent,
45
+ FeatureEvent,
46
+ ClientFeatureEventPayload,
44
47
  } from '../metrics.types';
45
48
  import CallDiagnosticEventsBatcher from './call-diagnostic-metrics-batcher';
46
49
  import PreLoginMetricsBatcher from '../prelogin-metrics-batcher';
@@ -58,6 +61,8 @@ import {
58
61
  AUTHENTICATION_FAILED_CODE,
59
62
  WEBEX_SUB_SERVICE_TYPES,
60
63
  SDP_OFFER_CREATION_ERROR_MAP,
64
+ CALL_FEATURE_LOG_IDENTIFIER,
65
+ CALL_FEATURE_EVENT_FAILED_TO_SEND,
61
66
  } from './config';
62
67
 
63
68
  const {getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
@@ -97,7 +102,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
97
102
  private hasLoggedBrowserSerial: boolean;
98
103
  private device: any;
99
104
  private delayedClientEvents: DelayedClientEvent[] = [];
105
+ private delayedClientFeatureEvents: DelayedClientFeatureEvent[] = [];
100
106
  private eventErrorCache: WeakMap<any, any> = new WeakMap();
107
+ private isMercuryConnected = false;
101
108
 
102
109
  // the default validator before piping an event to the batcher
103
110
  // this function can be overridden by the user
@@ -150,6 +157,16 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
150
157
  return undefined;
151
158
  }
152
159
 
160
+ /**
161
+ * Sets mercury connected status for event data object in CA events
162
+ * @public
163
+ * @param status - boolean value indicating mercury connection status
164
+ * @return {void}
165
+ */
166
+ public setMercuryConnectedStatus(status: boolean): void {
167
+ this.isMercuryConnected = status;
168
+ }
169
+
153
170
  /**
154
171
  * Returns meeting's subServiceType
155
172
  * @param meeting
@@ -416,12 +433,97 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
416
433
  }
417
434
 
418
435
  /**
419
- * TODO: NOT IMPLEMENTED
436
+ * Create feature event
437
+ * @param name
438
+ * @param payload
439
+ * @param options
440
+ * @returns
441
+ */
442
+ private prepareClientFeatureEvent({
443
+ name,
444
+ payload,
445
+ options,
446
+ }: {
447
+ name: FeatureEvent['name'];
448
+ payload?: ClientFeatureEventPayload;
449
+ options?: SubmitClientEventOptions;
450
+ }) {
451
+ const {meetingId, correlationId} = options;
452
+ let featureEventObject: FeatureEvent['payload'];
453
+
454
+ // events that will most likely happen in join phase
455
+ if (meetingId) {
456
+ featureEventObject = this.createFeatureEventObjectInMeeting({name, options});
457
+ } else {
458
+ throw new Error('Not implemented');
459
+ }
460
+
461
+ // merge any new properties, or override existing ones
462
+ featureEventObject = merge(featureEventObject, payload);
463
+
464
+ // append client event data to the call diagnostic event
465
+ const featureEvent = this.prepareDiagnosticEvent(featureEventObject, options);
466
+
467
+ return featureEvent;
468
+ }
469
+
470
+ /**
420
471
  * Submit Feature Event
472
+ * submit to business_ucf
421
473
  * @returns
422
474
  */
423
- public submitFeatureEvent() {
424
- throw Error('Not implemented');
475
+ public submitFeatureEvent({
476
+ name,
477
+ payload,
478
+ options,
479
+ delaySubmitEvent,
480
+ }: {
481
+ name: FeatureEvent['name'];
482
+ payload?: ClientFeatureEventPayload;
483
+ options?: SubmitClientEventOptions;
484
+ delaySubmitEvent?: boolean;
485
+ }) {
486
+ if (delaySubmitEvent) {
487
+ // Preserve the time when the event was triggered if delaying the submission to Call Features
488
+ const delayedOptions = {
489
+ ...options,
490
+ triggeredTime: new Date().toISOString(),
491
+ };
492
+
493
+ this.delayedClientFeatureEvents.push({
494
+ name,
495
+ payload,
496
+ options: delayedOptions,
497
+ });
498
+
499
+ return Promise.resolve();
500
+ }
501
+
502
+ this.logger.log(
503
+ CALL_FEATURE_LOG_IDENTIFIER,
504
+ 'CallFeatureMetrics: @submitFeatureEvent. Submit Client Feature Event CA event.',
505
+ `name: ${name}`
506
+ );
507
+ const featureEvent = this.prepareClientFeatureEvent({name, payload, options});
508
+
509
+ this.validator({type: 'ce', event: featureEvent});
510
+
511
+ return this.submitToCallFeatures(featureEvent);
512
+ }
513
+
514
+ /**
515
+ * Submit Feature Event
516
+ * type is business
517
+ * @param event
518
+ */
519
+ submitToCallFeatures(event: Event): Promise<any> {
520
+ // build metrics-a event type
521
+ const finalEvent = {
522
+ eventPayload: event,
523
+ type: ['business'],
524
+ };
525
+
526
+ return this.callDiagnosticEventsBatcher.request(finalEvent);
425
527
  }
426
528
 
427
529
  /**
@@ -680,20 +782,20 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
680
782
  }
681
783
 
682
784
  /**
683
- * Create client event object for in meeting events
684
- * @param arg - create args
685
- * @param arg.event - event key
686
- * @param arg.options - options
785
+ * Create common object for in meeting events
786
+ * @param name
787
+ * @param options
788
+ * @param eventType - 'client' | 'feature'
687
789
  * @returns object
688
790
  */
689
- private createClientEventObjectInMeeting({
791
+ private createCommonEventObjectInMeeting({
690
792
  name,
691
793
  options,
692
- errors,
794
+ eventType = 'client',
693
795
  }: {
694
- name: ClientEvent['name'];
796
+ name: string;
695
797
  options?: SubmitClientEventOptions;
696
- errors?: ClientEventPayloadError;
798
+ eventType?: 'client' | 'feature';
697
799
  }) {
698
800
  const {
699
801
  meetingId,
@@ -708,16 +810,21 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
708
810
 
709
811
  if (!meeting) {
710
812
  console.warn(
711
- 'Attempt to send client event but no meeting was found...',
813
+ 'Attempt to send common event but no meeting was found...',
712
814
  `name: ${name}, meetingId: ${meetingId}`
713
815
  );
714
816
  // @ts-ignore
715
- this.webex.internal.metrics.submitClientMetrics(CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND, {
716
- fields: {
717
- meetingId,
718
- name,
719
- },
720
- });
817
+ this.webex.internal.metrics.submitClientMetrics(
818
+ eventType === 'feature'
819
+ ? CALL_FEATURE_EVENT_FAILED_TO_SEND
820
+ : CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND,
821
+ {
822
+ fields: {
823
+ meetingId,
824
+ name,
825
+ },
826
+ }
827
+ );
721
828
 
722
829
  return undefined;
723
830
  }
@@ -731,12 +838,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
731
838
  sessionCorrelationId,
732
839
  });
733
840
 
734
- // create client event object
735
- const clientEventObject: ClientEvent['payload'] = {
841
+ // create common event object structur
842
+ const commonEventObject = {
736
843
  name,
737
844
  canProceed: true,
738
845
  identifiers,
739
- errors,
740
846
  eventData: {
741
847
  webClientDomain: window.location.hostname,
742
848
  },
@@ -757,18 +863,80 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
757
863
 
758
864
  const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
759
865
  if (joinFlowVersion) {
760
- clientEventObject.joinFlowVersion = joinFlowVersion;
866
+ // @ts-ignore
867
+ commonEventObject.joinFlowVersion = joinFlowVersion;
761
868
  }
762
869
  const meetingJoinedTime = meeting.isoLocalClientMeetingJoinTime;
763
870
  if (meetingJoinedTime) {
764
- clientEventObject.meetingJoinedTime = meetingJoinedTime;
871
+ // @ts-ignore
872
+ commonEventObject.meetingJoinedTime = meetingJoinedTime;
765
873
  }
766
874
 
767
875
  if (options.meetingJoinPhase) {
768
- clientEventObject.meetingJoinPhase = options.meetingJoinPhase;
876
+ // @ts-ignore
877
+ commonEventObject.meetingJoinPhase = options.meetingJoinPhase;
769
878
  }
770
879
 
771
- return clientEventObject;
880
+ return commonEventObject;
881
+ }
882
+
883
+ /**
884
+ * Create client event object for in meeting events
885
+ * @param arg - create args
886
+ * @param arg.event - event key
887
+ * @param arg.options - options
888
+ * @returns object
889
+ */
890
+ private createClientEventObjectInMeeting({
891
+ name,
892
+ options,
893
+ errors,
894
+ }: {
895
+ name: ClientEvent['name'];
896
+ options?: SubmitClientEventOptions;
897
+ errors?: ClientEventPayloadError;
898
+ }) {
899
+ const commonObject = this.createCommonEventObjectInMeeting({
900
+ name,
901
+ options,
902
+ eventType: 'client',
903
+ });
904
+ if (!commonObject) return undefined;
905
+
906
+ return {
907
+ ...commonObject,
908
+ errors,
909
+ eventData: {
910
+ ...commonObject.eventData,
911
+ isMercuryConnected: this.isMercuryConnected,
912
+ },
913
+ } as ClientEvent['payload'];
914
+ }
915
+
916
+ /**
917
+ * Create feature event object for in meeting function event
918
+ * @param name
919
+ * @param options
920
+ * @returns object
921
+ */
922
+ private createFeatureEventObjectInMeeting({
923
+ name,
924
+ options,
925
+ }: {
926
+ name: FeatureEvent['name'];
927
+ options?: SubmitClientEventOptions;
928
+ }) {
929
+ const commonObject = this.createCommonEventObjectInMeeting({
930
+ name,
931
+ options,
932
+ eventType: 'feature',
933
+ });
934
+ if (!commonObject) return undefined;
935
+
936
+ return {
937
+ ...commonObject,
938
+ key: 'UcfFeatureUsage',
939
+ } as FeatureEvent['payload'];
772
940
  }
773
941
 
774
942
  /**
@@ -807,6 +975,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
807
975
  identifiers,
808
976
  eventData: {
809
977
  webClientDomain: window.location.hostname,
978
+ isMercuryConnected: this.isMercuryConnected,
810
979
  },
811
980
  loginType: this.getCurLoginType(),
812
981
  // @ts-ignore
@@ -964,6 +1133,31 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
964
1133
  return Promise.all(promises);
965
1134
  }
966
1135
 
1136
+ /**
1137
+ * Submit Delayed feature Event CA events. Clears submitDelayedClientFeatureEvents array after submission.
1138
+ */
1139
+ public submitDelayedClientFeatureEvents(overrides?: Partial<DelayedClientEvent['options']>) {
1140
+ this.logger.log(
1141
+ CALL_FEATURE_LOG_IDENTIFIER,
1142
+ 'CallDiagnosticMetrics: @submitDelayedClientFeatureEvents. Submitting delayed feature events.'
1143
+ );
1144
+
1145
+ if (this.delayedClientFeatureEvents.length === 0) {
1146
+ return Promise.resolve();
1147
+ }
1148
+
1149
+ const promises = this.delayedClientFeatureEvents.map((delayedSubmitClientEventParams) => {
1150
+ const {name, payload, options} = delayedSubmitClientEventParams;
1151
+ const optionsWithOverrides: DelayedClientEvent['options'] = {...options, ...overrides};
1152
+
1153
+ return this.submitFeatureEvent({name, payload, options: optionsWithOverrides});
1154
+ });
1155
+
1156
+ this.delayedClientFeatureEvents = [];
1157
+
1158
+ return Promise.all(promises);
1159
+ }
1160
+
967
1161
  /**
968
1162
  * Prepare the event and send the request to metrics-a service.
969
1163
  * @param event
@@ -257,6 +257,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
257
257
  switch (eventName) {
258
258
  case 'client.webexapp.launched':
259
259
  joinTimes.downloadTime = cdl.getDownloadTimeJMT();
260
+ joinTimes.pageJmt = cdl.getPageJMT();
260
261
  break;
261
262
  case 'client.login.end':
262
263
  joinTimes.otherAppApiReqResp = cdl.getOtherAppApiReqResp();
@@ -267,6 +268,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
267
268
  joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
268
269
  joinTimes.refreshCaptchaServiceReqResp = cdl.getRefreshCaptchaReqResp();
269
270
  joinTimes.downloadIntelligenceModelsReqResp = cdl.getDownloadIntelligenceModelsReqResp();
271
+ joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
270
272
  break;
271
273
 
272
274
  case 'client.call.initiated':
@@ -287,6 +289,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
287
289
  joinTimes.totalJmt = cdl.getTotalJMT();
288
290
  joinTimes.clientJmt = cdl.getClientJMT();
289
291
  joinTimes.downloadTime = cdl.getDownloadTimeJMT();
292
+ joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
293
+ joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
290
294
  break;
291
295
 
292
296
  case 'client.ice.end':
@@ -307,6 +311,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
307
311
  joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
308
312
  joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
309
313
  joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
314
+ joinTimes.totalMediaJMTWithUserDelay = cdl.getTotalMediaJMTWithUserDelay();
315
+ joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
310
316
  break;
311
317
 
312
318
  case 'client.media.tx.start':
@@ -5,6 +5,7 @@
5
5
  import {ClientEventError, ClientSubServiceType} from '../metrics.types';
6
6
 
7
7
  export const CALL_DIAGNOSTIC_LOG_IDENTIFIER = 'call-diagnostic-events -> ';
8
+ export const CALL_FEATURE_LOG_IDENTIFIER = 'call-diagnostic-events-feature -> ';
8
9
 
9
10
  export const AUTHENTICATION_FAILED_CODE = 1010;
10
11
  export const NETWORK_ERROR = 1026;
@@ -738,3 +739,5 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
738
739
  };
739
740
 
740
741
  export const CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND = 'js_sdk_call_diagnostic_event_failed_to_send';
742
+
743
+ export const CALL_FEATURE_EVENT_FAILED_TO_SEND = 'js_sdk_call_feature_event_failed_to_send';
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  SubmitBusinessEvent,
19
19
  SubmitMQE,
20
20
  PreComputedLatencies,
21
+ SubmitFeatureEvent,
21
22
  } from './metrics.types';
22
23
  import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
23
24
  import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
@@ -61,4 +62,5 @@ export type {
61
62
  SubmitOperationalEvent,
62
63
  SubmitBusinessEvent,
63
64
  PreComputedLatencies,
65
+ SubmitFeatureEvent,
64
66
  };
@@ -2,9 +2,12 @@ import {
2
2
  ClientEvent as RawClientEvent,
3
3
  Event as RawEvent,
4
4
  MediaQualityEvent as RawMediaQualityEvent,
5
+ FeatureEvent as RawFeatureEvent,
5
6
  } from '@webex/event-dictionary-ts';
6
7
 
7
- export type Event = Omit<RawEvent, 'event'> & {event: RawClientEvent | RawMediaQualityEvent};
8
+ export type Event = Omit<RawEvent, 'event'> & {
9
+ event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;
10
+ };
8
11
 
9
12
  export type ClientEventError = NonNullable<RawClientEvent['errors']>[0];
10
13
 
@@ -212,10 +215,9 @@ export type BehavioralEvent = TaggedEvent;
212
215
  export type OperationalEvent = TaggedEvent;
213
216
 
214
217
  export interface FeatureEvent {
215
- // TODO: not implemented
216
- name: never;
217
- payload?: never;
218
- options?: never;
218
+ name: RawFeatureEvent['name'];
219
+ payload?: RawFeatureEvent;
220
+ options?: SubmitClientEventOptions;
219
221
  }
220
222
 
221
223
  export interface MediaQualityEvent {
@@ -251,6 +253,8 @@ export type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;
251
253
  export type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];
252
254
  export type ClientEventPayloadError = ClientEvent['payload']['errors'];
253
255
 
256
+ export type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;
257
+
254
258
  export type MediaQualityEventAudioSetupDelayPayload = NonNullable<
255
259
  MediaQualityEvent['payload']
256
260
  >['audioSetupDelay'];
@@ -311,6 +315,7 @@ export type PreComputedLatencies =
311
315
  | 'internal.download.time'
312
316
  | 'internal.get.cluster.time'
313
317
  | 'internal.click.to.interstitial'
318
+ | 'internal.click.to.interstitial.with.user.delay'
314
319
  | 'internal.refresh.captcha.time'
315
320
  | 'internal.exchange.ci.token.time'
316
321
  | 'internal.get.u2c.time'
@@ -339,3 +344,15 @@ export interface DelayedClientEvent {
339
344
  payload?: RecursivePartial<ClientEvent['payload']>;
340
345
  options?: SubmitClientEventOptions;
341
346
  }
347
+
348
+ export type SubmitFeatureEvent = (args: {
349
+ name: FeatureEvent['name'];
350
+ payload?: RecursivePartial<FeatureEvent['payload']>;
351
+ options?: SubmitClientEventOptions;
352
+ }) => Promise<any>;
353
+
354
+ export interface DelayedClientFeatureEvent {
355
+ name: FeatureEvent['name'];
356
+ payload?: RecursivePartial<FeatureEvent['payload']>;
357
+ options?: SubmitClientEventOptions;
358
+ }
@@ -23,6 +23,7 @@ import {
23
23
  SubmitClientEventOptions,
24
24
  Table,
25
25
  DelayedClientEvent,
26
+ DelayedClientFeatureEvent,
26
27
  } from './metrics.types';
27
28
  import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
28
29
  import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
@@ -51,11 +52,18 @@ class Metrics extends WebexPlugin {
51
52
  */
52
53
  delaySubmitClientEvents = false;
53
54
 
55
+ /**
56
+ * Whether or not to delay the submission of feature events.
57
+ */
58
+ delaySubmitClientFeatureEvents = false;
59
+
54
60
  /**
55
61
  * Overrides for delayed client events. E.g. if you want to override the correlationId for all delayed client events, you can set this to { correlationId: 'newCorrelationId' }
56
62
  */
57
63
  delayedClientEventsOverrides: Partial<DelayedClientEvent['options']> = {};
58
64
 
65
+ delayedClientFeatureEventsOverrides: Partial<DelayedClientFeatureEvent['options']> = {};
66
+
59
67
  /**
60
68
  * Constructor
61
69
  * @param args
@@ -275,7 +283,25 @@ class Metrics extends WebexPlugin {
275
283
  payload?: RecursivePartial<FeatureEvent['payload']>;
276
284
  options: any;
277
285
  }) {
278
- throw new Error('Not implemented.');
286
+ if (!this.callDiagnosticLatencies || !this.callDiagnosticMetrics) {
287
+ // @ts-ignore
288
+ this.webex.logger.log(
289
+ `NewMetrics: @submitFeatureEvent. Attempted to submit before webex.ready. Event name: ${name}`
290
+ );
291
+
292
+ return Promise.resolve();
293
+ }
294
+ this.callDiagnosticLatencies.saveTimestamp({
295
+ key: name,
296
+ options: {meetingId: options?.meetingId},
297
+ });
298
+
299
+ return this.callDiagnosticMetrics.submitFeatureEvent({
300
+ name,
301
+ payload,
302
+ options,
303
+ delaySubmitEvent: this.delaySubmitClientFeatureEvents,
304
+ });
279
305
  }
280
306
 
281
307
  /**
@@ -432,6 +458,31 @@ class Metrics extends WebexPlugin {
432
458
 
433
459
  return Promise.resolve();
434
460
  }
461
+
462
+ /**
463
+ * Sets the value of setDelaySubmitClientFeatureEvents.
464
+ * If set to true, feature events will be delayed until submitDelayedClientFeatureEvents is called.
465
+ * If set to false, delayed feature events will be submitted.
466
+ *
467
+ * @param {object} options - {shouldDelay: A boolean value indicating whether to delay the submission of feature events,
468
+ * overrides: An object containing overrides for the feature events}
469
+ */
470
+ public setDelaySubmitClientFeatureEvents({
471
+ shouldDelay,
472
+ overrides,
473
+ }: {
474
+ shouldDelay: boolean;
475
+ overrides?: Partial<DelayedClientFeatureEvent['options']>;
476
+ }) {
477
+ this.delaySubmitClientFeatureEvents = shouldDelay;
478
+ this.delayedClientFeatureEventsOverrides = overrides || {};
479
+
480
+ if (this.isReady && !shouldDelay) {
481
+ return this.callDiagnosticMetrics.submitDelayedClientFeatureEvents(overrides);
482
+ }
483
+
484
+ return Promise.resolve();
485
+ }
435
486
  }
436
487
 
437
488
  export default Metrics;
@@ -108,6 +108,8 @@ describe('plugin-metrics', () => {
108
108
  .returns(10);
109
109
  webex.internal.newMetrics.callDiagnosticLatencies.getDownloadIntelligenceModelsReqResp =
110
110
  sinon.stub().returns(42);
111
+ webex.internal.newMetrics.callDiagnosticLatencies.getClickToInterstitialWithUserDelay =
112
+ sinon.stub().returns(12);
111
113
 
112
114
  const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
113
115
  //@ts-ignore
@@ -127,6 +129,7 @@ describe('plugin-metrics', () => {
127
129
  meetingInfoReqResp: 10,
128
130
  refreshCaptchaServiceReqResp: 10,
129
131
  downloadIntelligenceModelsReqResp: 42,
132
+ clickToInterstitialWithUserDelay: 12,
130
133
  },
131
134
  });
132
135
  assert.lengthOf(
@@ -183,9 +186,15 @@ describe('plugin-metrics', () => {
183
186
  webex.internal.newMetrics.callDiagnosticLatencies.getCallInitJoinReq = sinon
184
187
  .stub()
185
188
  .returns(10);
186
- webex.internal.newMetrics.callDiagnosticLatencies.getDownloadTimeJMT = sinon
189
+ webex.internal.newMetrics.callDiagnosticLatencies.getDownloadTimeJMT = sinon
187
190
  .stub()
188
191
  .returns(100);
192
+ webex.internal.newMetrics.callDiagnosticLatencies.getClickToInterstitialWithUserDelay = sinon
193
+ .stub()
194
+ .returns(43);
195
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalJMTWithUserDelay = sinon
196
+ .stub()
197
+ .returns(64);
189
198
  const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
190
199
  //@ts-ignore
191
200
  {event: {name: 'client.locus.join.response'}}
@@ -209,6 +218,8 @@ describe('plugin-metrics', () => {
209
218
  totalJmt: 20,
210
219
  clientJmt: 5,
211
220
  downloadTime: 100,
221
+ clickToInterstitialWithUserDelay: 43,
222
+ totalJMTWithUserDelay: 64,
212
223
  },
213
224
  });
214
225
  assert.lengthOf(
@@ -338,6 +349,12 @@ describe('plugin-metrics', () => {
338
349
  webex.internal.newMetrics.callDiagnosticLatencies.getStayLobbyTime = sinon
339
350
  .stub()
340
351
  .returns(1);
352
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalMediaJMTWithUserDelay = sinon
353
+ .stub()
354
+ .returns(43);
355
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalJMTWithUserDelay = sinon
356
+ .stub()
357
+ .returns(64);
341
358
  const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
342
359
  //@ts-ignore
343
360
  {event: {name: 'client.media-engine.ready'}}
@@ -356,6 +373,8 @@ describe('plugin-metrics', () => {
356
373
  interstitialToMediaOKJMT: 22,
357
374
  callInitMediaEngineReady: 10,
358
375
  stayLobbyTime: 1,
376
+ totalMediaJMTWithUserDelay: 43,
377
+ totalJMTWithUserDelay: 64,
359
378
  },
360
379
  });
361
380
  assert.lengthOf(