@webex/internal-plugin-metrics 3.7.0 → 3.8.0
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.
- package/dist/business-metrics.js +74 -100
- package/dist/business-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +24 -15
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +56 -7
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/config.js +19 -12
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/call-diagnostic-events-batcher.js +59 -0
- package/dist/call-diagnostic-events-batcher.js.map +1 -0
- package/dist/generic-metrics.js +2 -2
- package/dist/generic-metrics.js.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.types.js.map +1 -1
- package/dist/new-metrics.js +28 -5
- package/dist/new-metrics.js.map +1 -1
- package/dist/types/business-metrics.d.ts +10 -28
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +14 -1
- package/dist/types/call-diagnostic/config.d.ts +2 -0
- package/dist/types/generic-metrics.d.ts +2 -2
- package/dist/types/metrics.types.d.ts +9 -1
- package/dist/types/new-metrics.d.ts +15 -3
- package/package.json +12 -12
- package/src/business-metrics.ts +66 -76
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +36 -14
- package/src/call-diagnostic/call-diagnostic-metrics.ts +72 -3
- package/src/call-diagnostic/config.ts +8 -0
- package/src/generic-metrics.ts +2 -2
- package/src/metrics.types.ts +10 -1
- package/src/new-metrics.ts +32 -4
- package/test/unit/spec/business/business-metrics.ts +2 -2
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +85 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +243 -2
- package/test/unit/spec/new-metrics.ts +23 -0
- package/dist/behavioral/behavioral-metrics.js +0 -199
- package/dist/behavioral/behavioral-metrics.js.map +0 -1
- package/dist/behavioral/config.js +0 -11
- package/dist/behavioral/config.js.map +0 -1
- package/dist/types/behavioral/behavioral-metrics.d.ts +0 -63
- package/dist/types/behavioral/config.d.ts +0 -1
|
@@ -83,11 +83,16 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
83
83
|
key === 'client.media.rx.start' ||
|
|
84
84
|
key === 'client.media.tx.start' ||
|
|
85
85
|
key === 'internal.client.meetinginfo.request' ||
|
|
86
|
-
key === 'internal.client.meetinginfo.response'
|
|
86
|
+
key === 'internal.client.meetinginfo.response' ||
|
|
87
|
+
key === 'client.media-engine.remote-sdp-received'
|
|
87
88
|
) {
|
|
88
89
|
this.saveFirstTimestampOnly(key, value);
|
|
89
90
|
} else {
|
|
90
91
|
this.latencyTimestamps.set(key, value);
|
|
92
|
+
// new offer/answer so reset the remote SDP timestamp
|
|
93
|
+
if (key === 'client.media-engine.local-sdp-generated') {
|
|
94
|
+
this.latencyTimestamps.delete('client.media-engine.remote-sdp-received');
|
|
95
|
+
}
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
|
|
@@ -146,7 +151,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
146
151
|
public getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames) {
|
|
147
152
|
const start = this.latencyTimestamps.get(a);
|
|
148
153
|
const end = this.latencyTimestamps.get(b);
|
|
149
|
-
|
|
154
|
+
|
|
155
|
+
if (typeof start === 'number' && typeof end === 'number') {
|
|
150
156
|
return end - start;
|
|
151
157
|
}
|
|
152
158
|
|
|
@@ -188,7 +194,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
188
194
|
public getU2CTime() {
|
|
189
195
|
const u2cLatency = this.precomputedLatencies.get('internal.get.u2c.time');
|
|
190
196
|
|
|
191
|
-
return u2cLatency ? Math.floor(u2cLatency) : undefined;
|
|
197
|
+
return typeof u2cLatency === 'number' ? Math.floor(u2cLatency) : undefined;
|
|
192
198
|
}
|
|
193
199
|
|
|
194
200
|
/**
|
|
@@ -291,7 +297,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
291
297
|
* @returns - latency
|
|
292
298
|
*/
|
|
293
299
|
public getPageJMT() {
|
|
294
|
-
|
|
300
|
+
const latency = this.precomputedLatencies.get('internal.client.pageJMT');
|
|
301
|
+
|
|
302
|
+
return typeof latency === 'number' ? latency : undefined;
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
/**
|
|
@@ -299,7 +307,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
299
307
|
* @returns - latency
|
|
300
308
|
*/
|
|
301
309
|
public getDownloadTimeJMT() {
|
|
302
|
-
|
|
310
|
+
const latency = this.precomputedLatencies.get('internal.download.time');
|
|
311
|
+
|
|
312
|
+
return typeof latency === 'number' ? latency : undefined;
|
|
303
313
|
}
|
|
304
314
|
|
|
305
315
|
/**
|
|
@@ -315,8 +325,15 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
315
325
|
);
|
|
316
326
|
}
|
|
317
327
|
|
|
318
|
-
|
|
319
|
-
|
|
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;
|
|
320
337
|
}
|
|
321
338
|
|
|
322
339
|
/**
|
|
@@ -353,7 +370,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
353
370
|
// get the first timestamp
|
|
354
371
|
const connectedMedia = this.latencyTimestamps.get('client.ice.end');
|
|
355
372
|
|
|
356
|
-
const
|
|
373
|
+
const lobbyTimeLatency = this.getStayLobbyTime();
|
|
374
|
+
const lobbyTime = typeof lobbyTimeLatency === 'number' ? lobbyTimeLatency : 0;
|
|
357
375
|
|
|
358
376
|
if (interstitialJoinClickTimestamp && connectedMedia) {
|
|
359
377
|
return connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
|
|
@@ -370,7 +388,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
370
388
|
const clickToInterstitial = this.getClickToInterstitial();
|
|
371
389
|
const interstitialToJoinOk = this.getInterstitialToJoinOK();
|
|
372
390
|
|
|
373
|
-
if (clickToInterstitial && interstitialToJoinOk) {
|
|
391
|
+
if (typeof clickToInterstitial === 'number' && typeof interstitialToJoinOk === 'number') {
|
|
374
392
|
return clickToInterstitial + interstitialToJoinOk;
|
|
375
393
|
}
|
|
376
394
|
|
|
@@ -422,7 +440,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
422
440
|
const interstitialToJoinOk = this.getInterstitialToJoinOK();
|
|
423
441
|
const joinConfJMT = this.getJoinConfJMT();
|
|
424
442
|
|
|
425
|
-
if (interstitialToJoinOk && joinConfJMT) {
|
|
443
|
+
if (typeof interstitialToJoinOk === 'number' && typeof joinConfJMT === 'number') {
|
|
426
444
|
return interstitialToJoinOk - joinConfJMT;
|
|
427
445
|
}
|
|
428
446
|
|
|
@@ -449,7 +467,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
449
467
|
public getReachabilityClustersReqResp() {
|
|
450
468
|
const reachablityClusterReqResp = this.precomputedLatencies.get('internal.get.cluster.time');
|
|
451
469
|
|
|
452
|
-
return
|
|
470
|
+
return typeof reachablityClusterReqResp === 'number'
|
|
471
|
+
? Math.floor(reachablityClusterReqResp)
|
|
472
|
+
: undefined;
|
|
453
473
|
}
|
|
454
474
|
|
|
455
475
|
/**
|
|
@@ -472,7 +492,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
472
492
|
public getExchangeCITokenJMT() {
|
|
473
493
|
const exchangeCITokenJMT = this.precomputedLatencies.get('internal.exchange.ci.token.time');
|
|
474
494
|
|
|
475
|
-
return exchangeCITokenJMT ? Math.floor(exchangeCITokenJMT) : undefined;
|
|
495
|
+
return typeof exchangeCITokenJMT === 'number' ? Math.floor(exchangeCITokenJMT) : undefined;
|
|
476
496
|
}
|
|
477
497
|
|
|
478
498
|
/**
|
|
@@ -481,7 +501,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
481
501
|
public getRefreshCaptchaReqResp() {
|
|
482
502
|
const refreshCaptchaReqResp = this.precomputedLatencies.get('internal.refresh.captcha.time');
|
|
483
503
|
|
|
484
|
-
return
|
|
504
|
+
return typeof refreshCaptchaReqResp === 'number'
|
|
505
|
+
? Math.floor(refreshCaptchaReqResp)
|
|
506
|
+
: undefined;
|
|
485
507
|
}
|
|
486
508
|
|
|
487
509
|
/**
|
|
@@ -493,7 +515,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
493
515
|
'internal.api.fetch.intelligence.models'
|
|
494
516
|
);
|
|
495
517
|
|
|
496
|
-
return downloadIntelligenceModelsReqResp
|
|
518
|
+
return typeof downloadIntelligenceModelsReqResp === 'number'
|
|
497
519
|
? Math.floor(downloadIntelligenceModelsReqResp)
|
|
498
520
|
: undefined;
|
|
499
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
|
|
@@ -165,9 +167,23 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
165
167
|
return WEBEX_SUB_SERVICE_TYPES.SCHEDULED_MEETING;
|
|
166
168
|
}
|
|
167
169
|
// if Scheduled, Webinar, not pmr - then Webinar
|
|
168
|
-
if (
|
|
170
|
+
if (
|
|
171
|
+
meetingInfo?.webexScheduled &&
|
|
172
|
+
meetingInfo?.enableEvent &&
|
|
173
|
+
!meetingInfo?.pmr &&
|
|
174
|
+
meetingInfo?.isConvergedWebinar
|
|
175
|
+
) {
|
|
169
176
|
return WEBEX_SUB_SERVICE_TYPES.WEBINAR;
|
|
170
177
|
}
|
|
178
|
+
// if Scheduled, Webinar enable webcast - then webcast
|
|
179
|
+
if (
|
|
180
|
+
meetingInfo?.webexScheduled &&
|
|
181
|
+
meetingInfo?.enableEvent &&
|
|
182
|
+
!meetingInfo?.pmr &&
|
|
183
|
+
meetingInfo?.isConvergedWebinarWebcast
|
|
184
|
+
) {
|
|
185
|
+
return WEBEX_SUB_SERVICE_TYPES.WEBCAST;
|
|
186
|
+
}
|
|
171
187
|
}
|
|
172
188
|
|
|
173
189
|
return undefined;
|
|
@@ -377,7 +393,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
377
393
|
* @returns
|
|
378
394
|
*/
|
|
379
395
|
prepareDiagnosticEvent(eventData: Event['event'], options: any) {
|
|
380
|
-
const {meetingId} = options;
|
|
396
|
+
const {meetingId, triggeredTime} = options;
|
|
381
397
|
const origin = this.getOrigin(options, meetingId);
|
|
382
398
|
|
|
383
399
|
const event: Event = {
|
|
@@ -385,7 +401,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
385
401
|
version: 1,
|
|
386
402
|
origin,
|
|
387
403
|
originTime: {
|
|
388
|
-
triggered: new Date().toISOString(),
|
|
404
|
+
triggered: triggeredTime || new Date().toISOString(),
|
|
389
405
|
// is overridden in prepareRequest batcher
|
|
390
406
|
sent: 'not_defined_yet',
|
|
391
407
|
},
|
|
@@ -716,6 +732,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
716
732
|
meetingId,
|
|
717
733
|
}),
|
|
718
734
|
webexSubServiceType: this.getSubServiceType(meeting),
|
|
735
|
+
// @ts-ignore
|
|
736
|
+
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
719
737
|
};
|
|
720
738
|
|
|
721
739
|
const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
|
|
@@ -723,6 +741,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
723
741
|
clientEventObject.joinFlowVersion = joinFlowVersion;
|
|
724
742
|
}
|
|
725
743
|
|
|
744
|
+
if (options.meetingJoinPhase) {
|
|
745
|
+
clientEventObject.meetingJoinPhase = options.meetingJoinPhase;
|
|
746
|
+
}
|
|
747
|
+
|
|
726
748
|
return clientEventObject;
|
|
727
749
|
}
|
|
728
750
|
|
|
@@ -764,12 +786,18 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
764
786
|
webClientDomain: window.location.hostname,
|
|
765
787
|
},
|
|
766
788
|
loginType: this.getCurLoginType(),
|
|
789
|
+
// @ts-ignore
|
|
790
|
+
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
767
791
|
};
|
|
768
792
|
|
|
769
793
|
if (options.joinFlowVersion) {
|
|
770
794
|
clientEventObject.joinFlowVersion = options.joinFlowVersion;
|
|
771
795
|
}
|
|
772
796
|
|
|
797
|
+
if (options.meetingJoinPhase) {
|
|
798
|
+
clientEventObject.meetingJoinPhase = options.meetingJoinPhase;
|
|
799
|
+
}
|
|
800
|
+
|
|
773
801
|
return clientEventObject;
|
|
774
802
|
}
|
|
775
803
|
|
|
@@ -834,17 +862,36 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
834
862
|
* @param arg.event - event key
|
|
835
863
|
* @param arg.payload - additional payload to be merged with default payload
|
|
836
864
|
* @param arg.options - payload
|
|
865
|
+
* @param arg.delaySubmitEvent - a boolean value indicating whether to delay the submission of client events.
|
|
837
866
|
* @throws
|
|
838
867
|
*/
|
|
839
868
|
public submitClientEvent({
|
|
840
869
|
name,
|
|
841
870
|
payload,
|
|
842
871
|
options,
|
|
872
|
+
delaySubmitEvent,
|
|
843
873
|
}: {
|
|
844
874
|
name: ClientEvent['name'];
|
|
845
875
|
payload?: ClientEventPayload;
|
|
846
876
|
options?: SubmitClientEventOptions;
|
|
877
|
+
delaySubmitEvent?: boolean;
|
|
847
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
|
+
|
|
848
895
|
this.logger.log(
|
|
849
896
|
CALL_DIAGNOSTIC_LOG_IDENTIFIER,
|
|
850
897
|
'CallDiagnosticMetrics: @submitClientEvent. Submit Client Event CA event.',
|
|
@@ -861,6 +908,28 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
861
908
|
return this.submitToCallDiagnostics(diagnosticEvent);
|
|
862
909
|
}
|
|
863
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
|
+
|
|
864
933
|
/**
|
|
865
934
|
* Prepare the event and send the request to metrics-a service.
|
|
866
935
|
* @param event
|
|
@@ -17,12 +17,14 @@ export const DTLS_HANDSHAKE_FAILED_CLIENT_CODE = 2008;
|
|
|
17
17
|
export const ICE_FAILED_WITH_TURN_TLS_CLIENT_CODE = 2010;
|
|
18
18
|
export const ICE_FAILED_WITHOUT_TURN_TLS_CLIENT_CODE = 2009;
|
|
19
19
|
export const ICE_AND_REACHABILITY_FAILED_CLIENT_CODE = 2011;
|
|
20
|
+
export const MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE = 2012;
|
|
20
21
|
export const WBX_APP_API_URL = 'wbxappapi'; // MeetingInfo WebexAppApi response object normally contains a body.url that includes the string 'wbxappapi'
|
|
21
22
|
|
|
22
23
|
export const WEBEX_SUB_SERVICE_TYPES: Record<string, ClientSubServiceType> = {
|
|
23
24
|
PMR: 'PMR',
|
|
24
25
|
SCHEDULED_MEETING: 'ScheduledMeeting',
|
|
25
26
|
WEBINAR: 'Webinar',
|
|
27
|
+
WEBCAST: 'Webcast',
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
// Found in https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
|
|
@@ -127,6 +129,7 @@ export const ERROR_DESCRIPTIONS = {
|
|
|
127
129
|
ICE_FAILED_WITHOUT_TURN_TLS: 'ICEFailedWithoutTURN_TLS',
|
|
128
130
|
ICE_FAILED_WITH_TURN_TLS: 'ICEFailedWithTURN_TLS',
|
|
129
131
|
ICE_AND_REACHABILITY_FAILED: 'ICEAndReachabilityFailed',
|
|
132
|
+
MULTISTREAM_NOT_AVAILABLE: 'MultistreamNotAvailable',
|
|
130
133
|
SDP_OFFER_CREATION_ERROR: 'SdpOfferCreationError',
|
|
131
134
|
SDP_OFFER_CREATION_ERROR_MISSING_CODEC: 'SdpOfferCreationErrorMissingCodec',
|
|
132
135
|
WDM_RESTRICTED_REGION: 'WdmRestrictedRegion',
|
|
@@ -409,6 +412,11 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
|
|
|
409
412
|
category: 'expected',
|
|
410
413
|
fatal: true,
|
|
411
414
|
},
|
|
415
|
+
[MULTISTREAM_NOT_AVAILABLE_CLIENT_CODE]: {
|
|
416
|
+
errorDescription: ERROR_DESCRIPTIONS.MULTISTREAM_NOT_AVAILABLE,
|
|
417
|
+
category: 'expected',
|
|
418
|
+
fatal: false,
|
|
419
|
+
},
|
|
412
420
|
2050: {
|
|
413
421
|
errorDescription: ERROR_DESCRIPTIONS.SDP_OFFER_CREATION_ERROR,
|
|
414
422
|
category: 'media',
|
package/src/generic-metrics.ts
CHANGED
|
@@ -37,7 +37,7 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Submit a
|
|
40
|
+
* Submit a business metric to our metrics endpoint.
|
|
41
41
|
* @param {string} kind of metric for logging
|
|
42
42
|
* @param {string} name of the metric
|
|
43
43
|
* @param {object} event
|
|
@@ -105,7 +105,7 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
* Returns true once we have the deviceId we need to submit behavioral/operational/
|
|
108
|
+
* Returns true once we have the deviceId we need to submit behavioral/operational/business events
|
|
109
109
|
* @returns {boolean}
|
|
110
110
|
*/
|
|
111
111
|
public isReadyToSubmitEvents(): boolean {
|
package/src/metrics.types.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type BrowserLaunchMethodType = NonNullable<
|
|
|
20
20
|
RawEvent['origin']['clientInfo']
|
|
21
21
|
>['browserLaunchMethod'];
|
|
22
22
|
|
|
23
|
-
export type MetricEventProduct = 'webex' | 'wxcc_desktop';
|
|
23
|
+
export type MetricEventProduct = 'webex' | 'wxcc_desktop' | 'wxcc_crm';
|
|
24
24
|
|
|
25
25
|
export type MetricEventAgent = 'user' | 'browser' | 'system' | 'sdk' | 'redux' | 'service' | 'api';
|
|
26
26
|
|
|
@@ -111,6 +111,7 @@ export type MetricEventVerb =
|
|
|
111
111
|
| 'exit';
|
|
112
112
|
|
|
113
113
|
export type MetricEventJoinFlowVersion = 'Other' | 'NewFTE';
|
|
114
|
+
export type MetricEventMeetingJoinPhase = 'pre-join' | 'join' | 'in-meeting';
|
|
114
115
|
|
|
115
116
|
export type SubmitClientEventOptions = {
|
|
116
117
|
meetingId?: string;
|
|
@@ -126,6 +127,8 @@ export type SubmitClientEventOptions = {
|
|
|
126
127
|
webexConferenceIdStr?: string;
|
|
127
128
|
globalMeetingId?: string;
|
|
128
129
|
joinFlowVersion?: MetricEventJoinFlowVersion;
|
|
130
|
+
meetingJoinPhase?: MetricEventMeetingJoinPhase;
|
|
131
|
+
triggeredTime?: string;
|
|
129
132
|
};
|
|
130
133
|
|
|
131
134
|
export type SubmitMQEOptions = {
|
|
@@ -317,3 +320,9 @@ export interface IMetricsAttributes {
|
|
|
317
320
|
meetingId?: string;
|
|
318
321
|
callId?: string;
|
|
319
322
|
}
|
|
323
|
+
|
|
324
|
+
export interface DelayedClientEvent {
|
|
325
|
+
name: ClientEvent['name'];
|
|
326
|
+
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
327
|
+
options?: SubmitClientEventOptions;
|
|
328
|
+
}
|
package/src/new-metrics.ts
CHANGED
|
@@ -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
|
|
@@ -141,7 +146,7 @@ class Metrics extends WebexPlugin {
|
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
/**
|
|
144
|
-
* @returns true once we have the deviceId we need to submit
|
|
149
|
+
* @returns true once we have the deviceId we need to submit business events
|
|
145
150
|
*/
|
|
146
151
|
isReadyToSubmitBusinessEvents() {
|
|
147
152
|
this.lazyBuildBusinessMetrics();
|
|
@@ -200,17 +205,19 @@ class Metrics extends WebexPlugin {
|
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
/**
|
|
203
|
-
*
|
|
208
|
+
* Business event
|
|
204
209
|
* @param args
|
|
205
210
|
*/
|
|
206
211
|
submitBusinessEvent({
|
|
207
212
|
name,
|
|
208
213
|
payload,
|
|
209
214
|
table,
|
|
215
|
+
metadata,
|
|
210
216
|
}: {
|
|
211
217
|
name: string;
|
|
212
218
|
payload: EventPayload;
|
|
213
219
|
table?: Table;
|
|
220
|
+
metadata?: EventPayload;
|
|
214
221
|
}) {
|
|
215
222
|
if (!this.isReady) {
|
|
216
223
|
// @ts-ignore
|
|
@@ -223,7 +230,7 @@ class Metrics extends WebexPlugin {
|
|
|
223
230
|
|
|
224
231
|
this.lazyBuildBusinessMetrics();
|
|
225
232
|
|
|
226
|
-
return this.businessMetrics.submitBusinessEvent({name, payload, table});
|
|
233
|
+
return this.businessMetrics.submitBusinessEvent({name, payload, table, metadata});
|
|
227
234
|
}
|
|
228
235
|
|
|
229
236
|
/**
|
|
@@ -288,7 +295,12 @@ class Metrics extends WebexPlugin {
|
|
|
288
295
|
options: {meetingId: options?.meetingId},
|
|
289
296
|
});
|
|
290
297
|
|
|
291
|
-
return this.callDiagnosticMetrics.submitClientEvent({
|
|
298
|
+
return this.callDiagnosticMetrics.submitClientEvent({
|
|
299
|
+
name,
|
|
300
|
+
payload,
|
|
301
|
+
options,
|
|
302
|
+
delaySubmitEvent: this.delaySubmitClientEvents,
|
|
303
|
+
});
|
|
292
304
|
}
|
|
293
305
|
|
|
294
306
|
/**
|
|
@@ -387,6 +399,22 @@ class Metrics extends WebexPlugin {
|
|
|
387
399
|
public isServiceErrorExpected(serviceErrorCode: number): boolean {
|
|
388
400
|
return this.callDiagnosticMetrics.isServiceErrorExpected(serviceErrorCode);
|
|
389
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
|
+
}
|
|
390
418
|
}
|
|
391
419
|
|
|
392
420
|
export default Metrics;
|
|
@@ -154,11 +154,12 @@ describe('internal-plugin-metrics', () => {
|
|
|
154
154
|
businessMetrics.clientMetricsBatcher.request = request;
|
|
155
155
|
|
|
156
156
|
assert.equal(requestCalls.length, 0)
|
|
157
|
-
businessMetrics.submitBusinessEvent({ name: "foobar", payload: {bar:"gee"}, table: 'business_metrics' })
|
|
157
|
+
businessMetrics.submitBusinessEvent({ name: "foobar", payload: {bar: "gee"}, table: 'business_metrics', metadata: {asdf: 'hjkl'} })
|
|
158
158
|
assert.equal(requestCalls.length, 1)
|
|
159
159
|
assert.deepEqual(requestCalls[0], {
|
|
160
160
|
eventPayload: {
|
|
161
161
|
key: 'foobar',
|
|
162
|
+
asdf: 'hjkl',
|
|
162
163
|
appType: 'Web Client',
|
|
163
164
|
client_timestamp: requestCalls[0].eventPayload.client_timestamp, // This is to bypass time check, which is checked below.
|
|
164
165
|
value: {
|
|
@@ -173,7 +174,6 @@ describe('internal-plugin-metrics', () => {
|
|
|
173
174
|
os: getOSNameInternal(),
|
|
174
175
|
app: {version: 'webex-version'},
|
|
175
176
|
device: {id: 'deviceId'},
|
|
176
|
-
locale: 'language',
|
|
177
177
|
}
|
|
178
178
|
},
|
|
179
179
|
type: ['business'],
|
|
@@ -283,6 +283,18 @@ describe('internal-plugin-metrics', () => {
|
|
|
283
283
|
cdl.saveTimestamp({key: 'internal.client.meetinginfo.response', value: 20});
|
|
284
284
|
assert.deepEqual(saveFirstTimestamp.callCount, 1);
|
|
285
285
|
});
|
|
286
|
+
|
|
287
|
+
it('calls saveFirstTimestamp for remote SDP received', () => {
|
|
288
|
+
const saveFirstTimestamp = sinon.stub(cdl, 'saveFirstTimestampOnly');
|
|
289
|
+
cdl.saveTimestamp({key: 'client.media-engine.remote-sdp-received', value: 10});
|
|
290
|
+
assert.deepEqual(saveFirstTimestamp.callCount, 1);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('clears timestamp for remote SDP received when local SDP generated', () => {
|
|
294
|
+
cdl.saveTimestamp({key: 'client.media-engine.remote-sdp-received', value: 10});
|
|
295
|
+
cdl.saveTimestamp({key: 'client.media-engine.local-sdp-generated', value: 20});
|
|
296
|
+
assert.isUndefined(cdl.latencyTimestamps.get('client.media-engine.remote-sdp-received'));
|
|
297
|
+
});
|
|
286
298
|
});
|
|
287
299
|
|
|
288
300
|
it('calculates getShowInterstitialTime correctly', () => {
|
|
@@ -425,6 +437,15 @@ describe('internal-plugin-metrics', () => {
|
|
|
425
437
|
assert.deepEqual(cdl.getClickToInterstitial(), 5);
|
|
426
438
|
});
|
|
427
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
|
+
|
|
428
449
|
it('calculates getInterstitialToJoinOK correctly', () => {
|
|
429
450
|
cdl.saveTimestamp({
|
|
430
451
|
key: 'internal.client.interstitial-window.click.joinbutton',
|
|
@@ -437,6 +458,18 @@ describe('internal-plugin-metrics', () => {
|
|
|
437
458
|
assert.deepEqual(cdl.getInterstitialToJoinOK(), 10);
|
|
438
459
|
});
|
|
439
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
|
+
|
|
440
473
|
it('calculates getCallInitMediaEngineReady correctly', () => {
|
|
441
474
|
cdl.saveTimestamp({
|
|
442
475
|
key: 'internal.client.interstitial-window.click.joinbutton',
|
|
@@ -469,6 +502,58 @@ describe('internal-plugin-metrics', () => {
|
|
|
469
502
|
assert.deepEqual(cdl.getTotalJMT(), 45);
|
|
470
503
|
});
|
|
471
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
|
+
|
|
472
557
|
it('calculates getTotalMediaJMT correctly', () => {
|
|
473
558
|
cdl.saveTimestamp({
|
|
474
559
|
key: 'internal.client.meeting.click.joinbutton',
|