@webex/internal-plugin-metrics 3.8.1-next.4 → 3.8.1-next.5

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.
@@ -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'];
@@ -340,3 +344,15 @@ export interface DelayedClientEvent {
340
344
  payload?: RecursivePartial<ClientEvent['payload']>;
341
345
  options?: SubmitClientEventOptions;
342
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;
@@ -185,7 +185,7 @@ describe('internal-plugin-metrics', () => {
185
185
  publicNetworkPrefix: '1.1.1.1',
186
186
  localNetworkPrefix: '1.1.1.1',
187
187
  os: getOSNameInternal(),
188
- osVersion: getOSVersion(),
188
+ osVersion: getOSVersion() || 'unknown',
189
189
  subClientType: 'WEB_APP',
190
190
  },
191
191
  environment: 'meeting_evn',
@@ -218,7 +218,7 @@ describe('internal-plugin-metrics', () => {
218
218
  publicNetworkPrefix: '1.1.1.1',
219
219
  localNetworkPrefix: '1.1.1.1',
220
220
  os: getOSNameInternal(),
221
- osVersion: getOSVersion(),
221
+ osVersion: getOSVersion() || 'unknown',
222
222
  subClientType: 'WEB_APP',
223
223
  clientLaunchMethod: 'url-handler',
224
224
  },
@@ -253,7 +253,7 @@ describe('internal-plugin-metrics', () => {
253
253
  publicNetworkPrefix: '1.1.1.1',
254
254
  localNetworkPrefix: '1.1.1.1',
255
255
  os: getOSNameInternal(),
256
- osVersion: getOSVersion(),
256
+ osVersion: getOSVersion() || 'unknown',
257
257
  subClientType: 'WEB_APP',
258
258
  clientLaunchMethod: 'url-handler',
259
259
  },
@@ -288,7 +288,7 @@ describe('internal-plugin-metrics', () => {
288
288
  publicNetworkPrefix: '1.1.1.1',
289
289
  localNetworkPrefix: '1.1.1.1',
290
290
  os: getOSNameInternal(),
291
- osVersion: getOSVersion(),
291
+ osVersion: getOSVersion() || 'unknown',
292
292
  subClientType: 'WEB_APP',
293
293
  clientLaunchMethod: 'url-handler',
294
294
  browserLaunchMethod: 'thinclient',
@@ -316,7 +316,7 @@ describe('internal-plugin-metrics', () => {
316
316
  publicNetworkPrefix: '1.1.1.1',
317
317
  localNetworkPrefix: '1.1.1.1',
318
318
  os: getOSNameInternal(),
319
- osVersion: getOSVersion(),
319
+ osVersion: getOSVersion() || 'unknown',
320
320
  subClientType: 'WEB_APP',
321
321
  },
322
322
  name: 'endpoint',
@@ -345,7 +345,7 @@ describe('internal-plugin-metrics', () => {
345
345
  majorVersion: 43,
346
346
  minorVersion: 9,
347
347
  os: getOSNameInternal(),
348
- osVersion: getOSVersion(),
348
+ osVersion: getOSVersion() || 'unknown',
349
349
  subClientType: 'WEB_APP',
350
350
  },
351
351
  environment: 'meeting_evn',
@@ -368,7 +368,7 @@ describe('internal-plugin-metrics', () => {
368
368
  publicNetworkPrefix: '1.3.4.0',
369
369
  localNetworkPrefix: undefined,
370
370
  os: getOSNameInternal(),
371
- osVersion: getOSVersion(),
371
+ osVersion: getOSVersion() || 'unknown',
372
372
  subClientType: 'WEB_APP',
373
373
  },
374
374
  name: 'endpoint',
@@ -2539,7 +2539,7 @@ describe('internal-plugin-metrics', () => {
2539
2539
  applicationSoftwareType: 'webex-js-sdk',
2540
2540
  applicationSoftwareVersion: 'webex-version',
2541
2541
  mediaEngineSoftwareType: 'browser',
2542
- mediaEngineSoftwareVersion: getOSVersion(),
2542
+ mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2543
2543
  startTime: now.toISOString(),
2544
2544
  },
2545
2545
  },
@@ -2578,7 +2578,7 @@ describe('internal-plugin-metrics', () => {
2578
2578
  applicationSoftwareType: 'webex-js-sdk',
2579
2579
  applicationSoftwareVersion: 'webex-version',
2580
2580
  mediaEngineSoftwareType: 'browser',
2581
- mediaEngineSoftwareVersion: getOSVersion(),
2581
+ mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2582
2582
  startTime: now.toISOString(),
2583
2583
  },
2584
2584
  },
@@ -2615,7 +2615,7 @@ describe('internal-plugin-metrics', () => {
2615
2615
  applicationSoftwareType: 'webex-js-sdk',
2616
2616
  applicationSoftwareVersion: 'webex-version',
2617
2617
  mediaEngineSoftwareType: 'browser',
2618
- mediaEngineSoftwareVersion: getOSVersion(),
2618
+ mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2619
2619
  startTime: now.toISOString(),
2620
2620
  },
2621
2621
  },
@@ -3466,7 +3466,7 @@ describe('internal-plugin-metrics', () => {
3466
3466
  localNetworkPrefix: '192.168.1.80',
3467
3467
  publicNetworkPrefix: '1.3.4.0',
3468
3468
  os: getOSNameInternal() || 'unknown',
3469
- osVersion: getOSVersion(),
3469
+ osVersion: getOSVersion() || 'unknown',
3470
3470
  subClientType: 'WEB_APP',
3471
3471
  },
3472
3472
  environment: 'meeting_evn',
@@ -3807,5 +3807,220 @@ describe('internal-plugin-metrics', () => {
3807
3807
  assert.notCalled(submitClientEventSpy);
3808
3808
  });
3809
3809
  });
3810
+
3811
+ describe('#submitFeatureEvent', () => {
3812
+ it('should submit feature event successfully with meetingId', () => {
3813
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
3814
+ const submitToCallFeaturesSpy = sinon.spy(cd, 'submitToCallFeatures');
3815
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
3816
+
3817
+ const options = {
3818
+ meetingId: fakeMeeting.id,
3819
+ };
3820
+ cd.setMercuryConnectedStatus(true);
3821
+
3822
+ cd.submitFeatureEvent({
3823
+ name: 'client.feature.meeting.summary',
3824
+ payload: {
3825
+ meetingSummaryInfo: {
3826
+ featureName: 'syncSystemMuteStatus',
3827
+ featureActions: [{
3828
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
3829
+ actionId: '14200',
3830
+ isInitialValue: false,
3831
+ clickCount: '1'
3832
+ }]
3833
+ },
3834
+ },
3835
+ options,
3836
+ });
3837
+
3838
+ assert.calledWith(
3839
+ prepareDiagnosticEventSpy,
3840
+ {
3841
+ name: 'client.feature.meeting.summary',
3842
+ canProceed: true,
3843
+ identifiers: {
3844
+ correlationId: 'correlationId',
3845
+ userId: 'userId',
3846
+ deviceId: 'deviceUrl',
3847
+ orgId: 'orgId',
3848
+ locusUrl: 'locus/url',
3849
+ locusId: 'url',
3850
+ locusStartTime: 'lastActive',
3851
+ },
3852
+ eventData: { webClientDomain: 'whatever'},
3853
+ userType: 'host',
3854
+ loginType: 'login-ci',
3855
+ isConvergedArchitectureEnabled: undefined,
3856
+ webexSubServiceType: undefined,
3857
+ webClientPreload: undefined,
3858
+ meetingSummaryInfo: {
3859
+ featureName: 'syncSystemMuteStatus',
3860
+ featureActions: [{
3861
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
3862
+ actionId: '14200',
3863
+ isInitialValue: false,
3864
+ clickCount: '1'
3865
+ }]
3866
+ },
3867
+ key: "UcfFeatureUsage",
3868
+ },
3869
+ options
3870
+ );
3871
+
3872
+ assert.calledWith(submitToCallFeaturesSpy, {
3873
+ eventId: 'my-fake-id',
3874
+ version: 1,
3875
+ origin: {
3876
+ origin: 'fake-origin',
3877
+ },
3878
+ event: {
3879
+ canProceed: true,
3880
+ eventData: { webClientDomain: 'whatever'},
3881
+ identifiers: {
3882
+ correlationId: 'correlationId',
3883
+ deviceId: 'deviceUrl',
3884
+ locusId: 'url',
3885
+ locusStartTime: 'lastActive',
3886
+ locusUrl: 'locus/url',
3887
+ orgId: 'orgId',
3888
+ userId: 'userId',
3889
+ },
3890
+ loginType: 'login-ci',
3891
+ name: 'client.feature.meeting.summary',
3892
+ userType: 'host',
3893
+ isConvergedArchitectureEnabled: undefined,
3894
+ webexSubServiceType: undefined,
3895
+ webClientPreload: undefined,
3896
+ meetingSummaryInfo: {
3897
+ featureName: 'syncSystemMuteStatus',
3898
+ featureActions: [{
3899
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
3900
+ actionId: '14200',
3901
+ isInitialValue: false,
3902
+ clickCount: '1'
3903
+ }]
3904
+ },
3905
+ key: "UcfFeatureUsage",
3906
+ },
3907
+ originTime: {
3908
+ sent: 'not_defined_yet',
3909
+ triggered: now.toISOString(),
3910
+ },
3911
+ senderCountryCode: 'UK',
3912
+ });
3913
+
3914
+ const webexLoggerLogCalls = webex.logger.log.getCalls();
3915
+ assert.deepEqual(webexLoggerLogCalls[1].args, [
3916
+ 'call-diagnostic-events-feature -> ',
3917
+ 'CallFeatureMetrics: @submitFeatureEvent. Submit Client Feature Event CA event.',
3918
+ `name: client.feature.meeting.summary`,
3919
+ ]);
3920
+ });
3921
+ });
3922
+
3923
+ describe('#submitDelayedClientFeatureEvents', () => {
3924
+ it('does not call submitFeatureEvent if there were no delayed events', () => {
3925
+ const submitFeatureEventSpy = sinon.spy(cd, 'submitFeatureEvent');
3926
+
3927
+ cd.submitDelayedClientFeatureEvents();
3928
+
3929
+ assert.notCalled(submitFeatureEventSpy);
3930
+ });
3931
+
3932
+ it('calls submitFeatureEvent for every delayed event and clears delayedClientFeatureEvents array', () => {
3933
+ const submitFeatureEventSpy = sinon.spy(cd, 'submitFeatureEvent');
3934
+ const submitToCallFeaturesSpy = sinon.spy(cd, 'submitToCallFeatures');
3935
+
3936
+ const options = {
3937
+ meetingId: 'meetingId',
3938
+ };
3939
+
3940
+ cd.submitFeatureEvent({
3941
+ name: 'client.feature.meeting.summary',
3942
+ options,
3943
+ payload: {
3944
+ meetingSummaryInfo: {
3945
+ featureName: 'syncSystemMuteStatus',
3946
+ featureActions: [{
3947
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
3948
+ actionId: '14200',
3949
+ isInitialValue: false,
3950
+ clickCount: '1'
3951
+ }]
3952
+ },
3953
+ },
3954
+ delaySubmitEvent: true,
3955
+ });
3956
+
3957
+ cd.submitFeatureEvent({
3958
+ name: 'client.feature.meeting.summary',
3959
+ options,
3960
+ payload: {
3961
+ meetingSummaryInfo: {
3962
+ featureName: 'syncSystemVideoStatus',
3963
+ featureActions: [{
3964
+ actionName: 'syncMeetingVideoUnmuteStatusToSystem',
3965
+ actionId: '13400',
3966
+ isInitialValue: false,
3967
+ clickCount: '1'
3968
+ }]
3969
+ },
3970
+ },
3971
+ delaySubmitEvent: true,
3972
+ });
3973
+
3974
+ assert.notCalled(submitToCallFeaturesSpy);
3975
+ assert.calledTwice(submitFeatureEventSpy);
3976
+ submitFeatureEventSpy.resetHistory();
3977
+
3978
+ cd.submitDelayedClientFeatureEvents();
3979
+
3980
+ assert.calledTwice(submitFeatureEventSpy);
3981
+ assert.calledWith(submitFeatureEventSpy.firstCall, {
3982
+ name: 'client.feature.meeting.summary',
3983
+ payload: {
3984
+ meetingSummaryInfo: {
3985
+ featureName: 'syncSystemMuteStatus',
3986
+ featureActions: [{
3987
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
3988
+ actionId: '14200',
3989
+ isInitialValue: false,
3990
+ clickCount: '1'
3991
+ }]
3992
+ },
3993
+ },
3994
+ options: {
3995
+ meetingId: 'meetingId',
3996
+ triggeredTime: now.toISOString(),
3997
+ },
3998
+ });
3999
+ assert.calledWith(submitFeatureEventSpy.secondCall, {
4000
+ name: 'client.feature.meeting.summary',
4001
+ payload: {
4002
+ meetingSummaryInfo: {
4003
+ featureName: 'syncSystemVideoStatus',
4004
+ featureActions: [{
4005
+ actionName: 'syncMeetingVideoUnmuteStatusToSystem',
4006
+ actionId: '13400',
4007
+ isInitialValue: false,
4008
+ clickCount: '1'
4009
+ }]
4010
+ },
4011
+ },
4012
+ options: {
4013
+ meetingId: 'meetingId',
4014
+ triggeredTime: now.toISOString(),
4015
+ },
4016
+ });
4017
+ submitFeatureEventSpy.resetHistory();
4018
+
4019
+ cd.submitDelayedClientFeatureEvents();
4020
+
4021
+ // should not call submitFeatureEventSpy again if delayedClientFeatureEvents was cleared
4022
+ assert.notCalled(submitFeatureEventSpy);
4023
+ });
4024
+ });
3810
4025
  });
3811
4026
  });
@@ -19,7 +19,7 @@ describe('internal-plugin-metrics', () => {
19
19
  }
20
20
  });
21
21
 
22
- describe('check submitClientEvent when webex is not ready', () => {
22
+ describe('check submitClientEvent, submitFeatureEvent when webex is not ready', () => {
23
23
  let webex;
24
24
  //@ts-ignore
25
25
  webex = mockWebex();
@@ -36,6 +36,30 @@ describe('internal-plugin-metrics', () => {
36
36
  'NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: client.alert.displayed'
37
37
  );
38
38
  });
39
+
40
+ it('checks the log', () => {
41
+ webex.internal.newMetrics.submitFeatureEvent({
42
+ name: 'client.feature.meeting.summary',
43
+ options: {
44
+ meetingId: '123',
45
+ },
46
+ payload: {
47
+ meetingSummaryInfo: {
48
+ featureName: 'syncSystemMuteStatus',
49
+ featureActions: [{
50
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
51
+ actionId: '14200',
52
+ isInitialValue: false,
53
+ clickCount: '1'
54
+ }]
55
+ },
56
+ },
57
+ });
58
+ assert.calledWith(
59
+ webex.logger.log,
60
+ 'NewMetrics: @submitFeatureEvent. Attempted to submit before webex.ready. Event name: client.feature.meeting.summary'
61
+ );
62
+ });
39
63
  });
40
64
 
41
65
  describe('new-metrics contstructor', () => {
@@ -65,6 +89,7 @@ describe('internal-plugin-metrics', () => {
65
89
  webex.internal.newMetrics.callDiagnosticMetrics.buildClientEventFetchRequestOptions =
66
90
  sinon.stub();
67
91
  webex.setTimingsAndFetch = sinon.stub();
92
+ webex.internal.newMetrics.callDiagnosticMetrics.submitFeatureEvent = sinon.stub();
68
93
  });
69
94
 
70
95
  afterEach(() => {
@@ -104,7 +129,7 @@ describe('internal-plugin-metrics', () => {
104
129
  metadata: { foo: 'bar' },
105
130
  });
106
131
  });
107
-
132
+
108
133
  it('submits Client Event successfully', () => {
109
134
  webex.internal.newMetrics.submitClientEvent({
110
135
  name: 'client.alert.displayed',
@@ -125,6 +150,46 @@ describe('internal-plugin-metrics', () => {
125
150
  });
126
151
  });
127
152
 
153
+ it('submits feature Event successfully', () => {
154
+ webex.internal.newMetrics.submitFeatureEvent({
155
+ name: 'client.feature.meeting.summary',
156
+ options: {
157
+ meetingId: '123',
158
+ },
159
+ payload: {
160
+ meetingSummaryInfo: {
161
+ featureName: 'syncSystemMuteStatus',
162
+ featureActions: [{
163
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
164
+ actionId: '14200',
165
+ isInitialValue: false,
166
+ clickCount: '1'
167
+ }]
168
+ },
169
+ },
170
+ });
171
+
172
+ assert.calledWith(webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp, {
173
+ key: 'client.feature.meeting.summary',
174
+ options: {meetingId: '123'},
175
+ });
176
+ assert.calledWith(webex.internal.newMetrics.callDiagnosticMetrics.submitFeatureEvent, {
177
+ name: 'client.feature.meeting.summary',
178
+ payload: {
179
+ meetingSummaryInfo: {
180
+ featureName: 'syncSystemMuteStatus',
181
+ featureActions: [{
182
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
183
+ actionId: '14200',
184
+ isInitialValue: false,
185
+ clickCount: '1'
186
+ }]
187
+ },
188
+ },
189
+ options: {meetingId: '123'},
190
+ delaySubmitEvent: false,
191
+ });
192
+ });
128
193
 
129
194
  it('submits MQE successfully', () => {
130
195
  webex.internal.newMetrics.submitMQE({