@webex/internal-plugin-metrics 3.12.0-next.3 → 3.12.0-next.31
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/batcher.js +3 -0
- package/dist/batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +23 -0
- package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +68 -51
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +80 -8
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +16 -3
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.types.js.map +1 -1
- package/dist/prelogin-metrics-batcher.js +23 -0
- package/dist/prelogin-metrics-batcher.js.map +1 -1
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +9 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +69 -13
- package/dist/types/call-diagnostic/config.d.ts +4 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/metrics.types.d.ts +2 -2
- package/package.json +11 -11
- package/src/batcher.js +4 -0
- package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +26 -0
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +140 -69
- package/src/call-diagnostic/call-diagnostic-metrics.ts +74 -1
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
- package/src/call-diagnostic/config.ts +14 -0
- package/src/config.js +1 -0
- package/src/metrics.types.ts +1 -1
- package/src/prelogin-metrics-batcher.ts +26 -0
- package/test/unit/spec/batcher.js +43 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +150 -2
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +243 -288
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +764 -159
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
- package/test/unit/spec/prelogin-metrics-batcher.ts +190 -36
|
@@ -193,7 +193,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
193
193
|
*/
|
|
194
194
|
public getShowInterstitialTime() {
|
|
195
195
|
return this.getDiffBetweenTimestamps(
|
|
196
|
-
'client.interstitial-window.
|
|
196
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
197
197
|
'internal.client.interstitial-window.click.joinbutton'
|
|
198
198
|
);
|
|
199
199
|
}
|
|
@@ -224,11 +224,17 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
224
224
|
* @returns - latency
|
|
225
225
|
*/
|
|
226
226
|
public getCallInitJoinReq() {
|
|
227
|
-
|
|
228
|
-
'internal.client.interstitial-window.
|
|
229
|
-
'client.locus.join.request'
|
|
230
|
-
{maximum: 1200000}
|
|
227
|
+
const interstitialShowedToJoinReq = this.getDiffBetweenTimestamps(
|
|
228
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
229
|
+
'client.locus.join.request'
|
|
231
230
|
);
|
|
231
|
+
const showInterstitialTime = this.getShowInterstitialTime() || 0;
|
|
232
|
+
|
|
233
|
+
if (typeof interstitialShowedToJoinReq !== 'number') {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return clamp(interstitialShowedToJoinReq - showInterstitialTime, 0, 1200000);
|
|
232
238
|
}
|
|
233
239
|
|
|
234
240
|
/**
|
|
@@ -303,7 +309,39 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
303
309
|
* @returns - latency
|
|
304
310
|
*/
|
|
305
311
|
public getStayLobbyTime() {
|
|
306
|
-
return this.getDiffBetweenTimestamps('client.
|
|
312
|
+
return this.getDiffBetweenTimestamps('client.lobby.entered', 'client.lobby.exited');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Stay lobby time capped by a certain timestamp.
|
|
317
|
+
* This is to handle the case where the target end timestamp could happen before the lobby is exited,
|
|
318
|
+
* for example media-engine.ready or client.ice.end
|
|
319
|
+
* This is supposed to be called AFTER the end timestamp happens
|
|
320
|
+
* @param endTimestampKey name of the target end event
|
|
321
|
+
* @returns - latency
|
|
322
|
+
*/
|
|
323
|
+
public getStayLobbyTimeCappedBy(endTimestampKey: MetricEventNames) {
|
|
324
|
+
const lobbyStartTimestamp = this.latencyTimestamps.get('client.lobby.entered'); // might not exist (some meetings don't have lobby)
|
|
325
|
+
|
|
326
|
+
if (typeof lobbyStartTimestamp !== 'number') {
|
|
327
|
+
// no lobby in the meeting, stayLobbyTime is 0
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const lobbyEndTimestamp = this.latencyTimestamps.get('client.lobby.exited'); // might not exist (if user still in lobby at the time of measurement)
|
|
332
|
+
const maximumEndTimestamp = this.latencyTimestamps.get(endTimestampKey); // must exist
|
|
333
|
+
|
|
334
|
+
if (typeof maximumEndTimestamp !== 'number') {
|
|
335
|
+
// the provided timestamp to be used as a cap should exist, return undefined if it doesn't
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const endTimestamp =
|
|
340
|
+
typeof lobbyEndTimestamp === 'number'
|
|
341
|
+
? Math.min(lobbyEndTimestamp, maximumEndTimestamp)
|
|
342
|
+
: maximumEndTimestamp;
|
|
343
|
+
|
|
344
|
+
return clamp(endTimestamp - lobbyStartTimestamp, 0, this.MAX_INTEGER);
|
|
307
345
|
}
|
|
308
346
|
|
|
309
347
|
/**
|
|
@@ -331,14 +369,6 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
331
369
|
* @returns - latency
|
|
332
370
|
*/
|
|
333
371
|
public getClickToInterstitial() {
|
|
334
|
-
// for normal join (where green join button exists before interstitial, i.e reminder, space list etc)
|
|
335
|
-
if (this.latencyTimestamps.get('internal.client.meeting.click.joinbutton')) {
|
|
336
|
-
return this.getDiffBetweenTimestamps(
|
|
337
|
-
'internal.client.meeting.click.joinbutton',
|
|
338
|
-
'internal.client.meeting.interstitial-window.showed'
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
372
|
const clickToInterstitialLatency = this.precomputedLatencies.get(
|
|
343
373
|
'internal.click.to.interstitial'
|
|
344
374
|
);
|
|
@@ -355,14 +385,6 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
355
385
|
* @returns - latency
|
|
356
386
|
*/
|
|
357
387
|
public getClickToInterstitialWithUserDelay() {
|
|
358
|
-
// for normal join (where green join button exists before interstitial, i.e reminder, space list etc)
|
|
359
|
-
if (this.latencyTimestamps.get('internal.client.meeting.click.joinbutton')) {
|
|
360
|
-
return this.getDiffBetweenTimestamps(
|
|
361
|
-
'internal.client.meeting.click.joinbutton',
|
|
362
|
-
'internal.client.meeting.interstitial-window.showed'
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
388
|
const clickToInterstitialWithUserDelayLatency = this.precomputedLatencies.get(
|
|
367
389
|
'internal.click.to.interstitial.with.user.delay'
|
|
368
390
|
);
|
|
@@ -379,10 +401,17 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
379
401
|
* @returns - latency
|
|
380
402
|
*/
|
|
381
403
|
public getInterstitialToJoinOK() {
|
|
382
|
-
|
|
383
|
-
'internal.client.interstitial-window.
|
|
404
|
+
const interstitialShowedToJoinResp = this.getDiffBetweenTimestamps(
|
|
405
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
384
406
|
'client.locus.join.response'
|
|
385
407
|
);
|
|
408
|
+
const showInterstitialTime = this.getShowInterstitialTime() || 0;
|
|
409
|
+
|
|
410
|
+
if (typeof interstitialShowedToJoinResp !== 'number') {
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return clamp(interstitialShowedToJoinResp - showInterstitialTime, 0, this.MAX_INTEGER);
|
|
386
415
|
}
|
|
387
416
|
|
|
388
417
|
/**
|
|
@@ -390,11 +419,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
390
419
|
* @returns - latency
|
|
391
420
|
*/
|
|
392
421
|
public getCallInitMediaEngineReady() {
|
|
393
|
-
return this.
|
|
394
|
-
'internal.client.interstitial-window.click.joinbutton',
|
|
395
|
-
'client.media-engine.ready',
|
|
396
|
-
{maximum: 1200000}
|
|
397
|
-
);
|
|
422
|
+
return this.getInterstitialToMediaOKJMT();
|
|
398
423
|
}
|
|
399
424
|
|
|
400
425
|
/**
|
|
@@ -402,20 +427,22 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
402
427
|
* @returns - latency
|
|
403
428
|
*/
|
|
404
429
|
public getInterstitialToMediaOKJMT() {
|
|
405
|
-
const
|
|
406
|
-
'internal.client.interstitial-window.
|
|
430
|
+
const interstitialShowedToIceEnd = this.getDiffBetweenTimestamps(
|
|
431
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
432
|
+
'client.ice.end'
|
|
407
433
|
);
|
|
434
|
+
const showInterstitialTime = this.getShowInterstitialTime() || 0;
|
|
435
|
+
const stayLobbyTimeCappedByIceEnd = this.getStayLobbyTimeCappedBy('client.ice.end');
|
|
408
436
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return clamp(interstitialToMediaOKJmt, 0, this.MAX_INTEGER);
|
|
437
|
+
if (
|
|
438
|
+
typeof interstitialShowedToIceEnd === 'number' &&
|
|
439
|
+
typeof stayLobbyTimeCappedByIceEnd === 'number'
|
|
440
|
+
) {
|
|
441
|
+
return clamp(
|
|
442
|
+
interstitialShowedToIceEnd - showInterstitialTime - stayLobbyTimeCappedByIceEnd,
|
|
443
|
+
0,
|
|
444
|
+
this.MAX_INTEGER
|
|
445
|
+
);
|
|
419
446
|
}
|
|
420
447
|
|
|
421
448
|
return undefined;
|
|
@@ -427,10 +454,21 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
427
454
|
*/
|
|
428
455
|
public getTotalJMT() {
|
|
429
456
|
const clickToInterstitial = this.getClickToInterstitial();
|
|
430
|
-
const
|
|
457
|
+
const interstitialShowedToJoinLocusResponse = this.getDiffBetweenTimestamps(
|
|
458
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
459
|
+
'client.locus.join.response'
|
|
460
|
+
);
|
|
461
|
+
const showInterstitialTime = this.getShowInterstitialTime() || 0;
|
|
431
462
|
|
|
432
|
-
if (
|
|
433
|
-
|
|
463
|
+
if (
|
|
464
|
+
typeof clickToInterstitial === 'number' &&
|
|
465
|
+
typeof interstitialShowedToJoinLocusResponse === 'number'
|
|
466
|
+
) {
|
|
467
|
+
return clamp(
|
|
468
|
+
clickToInterstitial + interstitialShowedToJoinLocusResponse - showInterstitialTime,
|
|
469
|
+
0,
|
|
470
|
+
this.MAX_INTEGER
|
|
471
|
+
);
|
|
434
472
|
}
|
|
435
473
|
|
|
436
474
|
return undefined;
|
|
@@ -442,13 +480,20 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
442
480
|
*/
|
|
443
481
|
public getTotalJMTWithUserDelay() {
|
|
444
482
|
const clickToInterstitialWithUserDelay = this.getClickToInterstitialWithUserDelay();
|
|
445
|
-
const
|
|
483
|
+
const interstitialShowedToJoinLocusResponse = this.getDiffBetweenTimestamps(
|
|
484
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
485
|
+
'client.locus.join.response'
|
|
486
|
+
);
|
|
446
487
|
|
|
447
488
|
if (
|
|
448
489
|
typeof clickToInterstitialWithUserDelay === 'number' &&
|
|
449
|
-
typeof
|
|
490
|
+
typeof interstitialShowedToJoinLocusResponse === 'number'
|
|
450
491
|
) {
|
|
451
|
-
return clamp(
|
|
492
|
+
return clamp(
|
|
493
|
+
clickToInterstitialWithUserDelay + interstitialShowedToJoinLocusResponse,
|
|
494
|
+
0,
|
|
495
|
+
this.MAX_INTEGER
|
|
496
|
+
);
|
|
452
497
|
}
|
|
453
498
|
|
|
454
499
|
return undefined;
|
|
@@ -475,22 +520,28 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
475
520
|
*/
|
|
476
521
|
public getTotalMediaJMT() {
|
|
477
522
|
const clickToInterstitial = this.getClickToInterstitial();
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
523
|
+
const interstitialShowedToMediaEngineReady = this.getDiffBetweenTimestamps(
|
|
524
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
525
|
+
'client.media-engine.ready'
|
|
526
|
+
);
|
|
527
|
+
const showInterstitialTime = this.getShowInterstitialTime() || 0;
|
|
528
|
+
const stayLobbyTimeCappedByMediaEngineReady = this.getStayLobbyTimeCappedBy(
|
|
529
|
+
'client.media-engine.ready'
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
if (
|
|
533
|
+
typeof clickToInterstitial === 'number' &&
|
|
534
|
+
typeof interstitialShowedToMediaEngineReady === 'number' &&
|
|
535
|
+
typeof stayLobbyTimeCappedByMediaEngineReady === 'number'
|
|
536
|
+
) {
|
|
537
|
+
return clamp(
|
|
538
|
+
clickToInterstitial +
|
|
539
|
+
interstitialShowedToMediaEngineReady -
|
|
540
|
+
showInterstitialTime -
|
|
541
|
+
stayLobbyTimeCappedByMediaEngineReady,
|
|
486
542
|
0,
|
|
487
|
-
|
|
543
|
+
this.MAX_INTEGER
|
|
488
544
|
);
|
|
489
|
-
if (this.getMeeting()?.allowMediaInLobby) {
|
|
490
|
-
return clamp(totalMediaJMT, 0, this.MAX_INTEGER);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return clamp(totalMediaJMT - lobbyTime, 0, this.MAX_INTEGER);
|
|
494
545
|
}
|
|
495
546
|
|
|
496
547
|
return undefined;
|
|
@@ -502,12 +553,17 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
502
553
|
*/
|
|
503
554
|
public getTotalMediaJMTWithUserDelay() {
|
|
504
555
|
const clickToInterstitialWithUserDelay = this.getClickToInterstitialWithUserDelay();
|
|
505
|
-
const
|
|
506
|
-
|
|
556
|
+
const interstitialShowedToMediaEngineReady = this.getDiffBetweenTimestamps(
|
|
557
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
558
|
+
'client.media-engine.ready'
|
|
559
|
+
);
|
|
507
560
|
|
|
508
|
-
if (
|
|
561
|
+
if (
|
|
562
|
+
typeof clickToInterstitialWithUserDelay === 'number' &&
|
|
563
|
+
typeof interstitialShowedToMediaEngineReady === 'number'
|
|
564
|
+
) {
|
|
509
565
|
return clamp(
|
|
510
|
-
clickToInterstitialWithUserDelay +
|
|
566
|
+
clickToInterstitialWithUserDelay + interstitialShowedToMediaEngineReady,
|
|
511
567
|
0,
|
|
512
568
|
this.MAX_INTEGER
|
|
513
569
|
);
|
|
@@ -521,11 +577,26 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
521
577
|
* @returns - latency
|
|
522
578
|
*/
|
|
523
579
|
public getClientJMT() {
|
|
524
|
-
const
|
|
525
|
-
|
|
580
|
+
const clickToInterstitialForClientJmt = this.precomputedLatencies.get(
|
|
581
|
+
'internal.click.to.interstitial.for.client.jmt'
|
|
582
|
+
);
|
|
583
|
+
const interstitialShowedToLocusJoinRequest = this.getDiffBetweenTimestamps(
|
|
584
|
+
'internal.client.meeting.interstitial-window.showed',
|
|
585
|
+
'client.locus.join.request'
|
|
586
|
+
);
|
|
587
|
+
const showInterstitialTime = this.getShowInterstitialTime() || 0;
|
|
526
588
|
|
|
527
|
-
if (
|
|
528
|
-
|
|
589
|
+
if (
|
|
590
|
+
typeof clickToInterstitialForClientJmt === 'number' &&
|
|
591
|
+
typeof interstitialShowedToLocusJoinRequest === 'number'
|
|
592
|
+
) {
|
|
593
|
+
return clamp(
|
|
594
|
+
clickToInterstitialForClientJmt +
|
|
595
|
+
interstitialShowedToLocusJoinRequest -
|
|
596
|
+
showInterstitialTime,
|
|
597
|
+
0,
|
|
598
|
+
this.MAX_INTEGER
|
|
599
|
+
);
|
|
529
600
|
}
|
|
530
601
|
|
|
531
602
|
return undefined;
|
|
@@ -108,6 +108,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
108
108
|
private isMercuryConnected = false;
|
|
109
109
|
private eventLimitTracker: Map<string, number> = new Map();
|
|
110
110
|
private eventLimitWarningsLogged: Set<string> = new Set();
|
|
111
|
+
private isTelemetryOptOutManual = false;
|
|
112
|
+
private isTelemetryOptOutAutomatic = false;
|
|
111
113
|
|
|
112
114
|
// the default validator before piping an event to the batcher
|
|
113
115
|
// this function can be overridden by the user
|
|
@@ -145,6 +147,58 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
145
147
|
return null;
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Returns the user activation state reported from the browser's navigator.userActivation API
|
|
152
|
+
* @returns object with hasBeenActive and isActive booleans, or undefined if unavailable
|
|
153
|
+
*/
|
|
154
|
+
getUserActivation(): {hasBeenActive: boolean; isActive: boolean} | undefined {
|
|
155
|
+
const userActivation =
|
|
156
|
+
typeof navigator !== 'undefined'
|
|
157
|
+
? (navigator as {userActivation?: {hasBeenActive: boolean; isActive: boolean}})
|
|
158
|
+
.userActivation
|
|
159
|
+
: undefined;
|
|
160
|
+
|
|
161
|
+
if (userActivation) {
|
|
162
|
+
return {
|
|
163
|
+
hasBeenActive: userActivation.hasBeenActive,
|
|
164
|
+
isActive: userActivation.isActive,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Returns the telemetryOptOut value of the current user
|
|
173
|
+
* @returns one of 'manual', 'automatic', undefined
|
|
174
|
+
*/
|
|
175
|
+
public getTelemetryOptOut() {
|
|
176
|
+
if (this.isTelemetryOptOutManual) {
|
|
177
|
+
return 'manual';
|
|
178
|
+
}
|
|
179
|
+
if (this.isTelemetryOptOutAutomatic) {
|
|
180
|
+
return 'automatic';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Sets the manual telemetry opt-out status for the current user
|
|
188
|
+
* @param value - boolean value indicating manual telemetry opt-out status
|
|
189
|
+
*/
|
|
190
|
+
public setIsTelemetryOptOutManual(value: boolean) {
|
|
191
|
+
this.isTelemetryOptOutManual = value;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Sets the automatic telemetry opt-out status for the current user
|
|
196
|
+
* @param value - boolean value indicating automatic telemetry opt-out status
|
|
197
|
+
*/
|
|
198
|
+
public setIsTelemetryOptOutAutomatic(value: boolean) {
|
|
199
|
+
this.isTelemetryOptOutAutomatic = value;
|
|
200
|
+
}
|
|
201
|
+
|
|
148
202
|
/**
|
|
149
203
|
* Returns if the meeting has converged architecture enabled
|
|
150
204
|
* @param options.meetingId
|
|
@@ -190,6 +244,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
190
244
|
|
|
191
245
|
// if ConvergedArchitecture enable and isConvergedWebinarWebcast -- then webcast
|
|
192
246
|
if (meetingInfo?.enableConvergedArchitecture && meetingInfo?.enableEvent) {
|
|
247
|
+
// if enableConvergedWebinarLargeScale - then large scale webinar
|
|
248
|
+
if (meetingInfo?.enableConvergedWebinarLargeScale) {
|
|
249
|
+
return WEBEX_SUB_SERVICE_TYPES.LARGE_SCALE_WEBINAR;
|
|
250
|
+
}
|
|
251
|
+
|
|
193
252
|
return meetingInfo?.isConvergedWebinarWebcast
|
|
194
253
|
? WEBEX_SUB_SERVICE_TYPES.WEBCAST
|
|
195
254
|
: WEBEX_SUB_SERVICE_TYPES.WEBINAR;
|
|
@@ -602,6 +661,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
602
661
|
mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
|
|
603
662
|
startTime: new Date().toISOString(),
|
|
604
663
|
},
|
|
664
|
+
webexSubServiceType: this.getSubServiceType(meeting),
|
|
605
665
|
};
|
|
606
666
|
|
|
607
667
|
// merge any new properties, or override existing ones
|
|
@@ -984,7 +1044,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
984
1044
|
sessionCorrelationId,
|
|
985
1045
|
});
|
|
986
1046
|
|
|
987
|
-
// create common event object
|
|
1047
|
+
// create common event object structure
|
|
988
1048
|
const commonEventObject = {
|
|
989
1049
|
name,
|
|
990
1050
|
canProceed: true,
|
|
@@ -997,6 +1057,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
997
1057
|
'loginType' in meeting.callStateForMetrics
|
|
998
1058
|
? meeting.callStateForMetrics.loginType
|
|
999
1059
|
: this.getCurLoginType(),
|
|
1060
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1000
1061
|
isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
|
|
1001
1062
|
meetingId,
|
|
1002
1063
|
}),
|
|
@@ -1005,6 +1066,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1005
1066
|
webexSubServiceType: this.getSubServiceType(meeting),
|
|
1006
1067
|
// @ts-ignore
|
|
1007
1068
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1069
|
+
isVipMeeting: meeting?.meetingInfo?.vipmeeting || false,
|
|
1070
|
+
isAutomatedUser:
|
|
1071
|
+
typeof window !== 'undefined' && typeof navigator !== 'undefined' && !!navigator?.webdriver, // if webdriver is true, it's most likely in a test environment
|
|
1072
|
+
userActivation: this.getUserActivation(),
|
|
1008
1073
|
};
|
|
1009
1074
|
|
|
1010
1075
|
const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
|
|
@@ -1124,8 +1189,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1124
1189
|
isMercuryConnected: this.isMercuryConnected,
|
|
1125
1190
|
},
|
|
1126
1191
|
loginType: this.getCurLoginType(),
|
|
1192
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1127
1193
|
// @ts-ignore
|
|
1128
1194
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1195
|
+
isAutomatedUser:
|
|
1196
|
+
typeof window !== 'undefined' && typeof navigator !== 'undefined' && !!navigator?.webdriver, // if webdriver is true, it's most likely in a test environment
|
|
1197
|
+
userActivation: this.getUserActivation(),
|
|
1129
1198
|
};
|
|
1130
1199
|
|
|
1131
1200
|
if (options.joinFlowVersion) {
|
|
@@ -1318,6 +1387,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1318
1387
|
const finalEvent = {
|
|
1319
1388
|
eventPayload: event,
|
|
1320
1389
|
type: ['diagnostic-event'],
|
|
1390
|
+
markTelemetryOptOutOnResponse: true,
|
|
1321
1391
|
};
|
|
1322
1392
|
|
|
1323
1393
|
return this.callDiagnosticEventsBatcher.request(finalEvent);
|
|
@@ -1327,6 +1397,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1327
1397
|
* Prepare the event and send the request to metrics-a service, pre login.
|
|
1328
1398
|
* @param event
|
|
1329
1399
|
* @param preLoginId
|
|
1400
|
+
* @param markTelemetryOptOutOnResponse
|
|
1330
1401
|
* @returns
|
|
1331
1402
|
*/
|
|
1332
1403
|
submitToCallDiagnosticsPreLogin = (event: Event, preLoginId?: string): Promise<any> => {
|
|
@@ -1334,7 +1405,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1334
1405
|
const finalEvent = {
|
|
1335
1406
|
eventPayload: event,
|
|
1336
1407
|
type: ['diagnostic-event'],
|
|
1408
|
+
markTelemetryOptOutOnResponse: true,
|
|
1337
1409
|
};
|
|
1410
|
+
|
|
1338
1411
|
this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
|
|
1339
1412
|
|
|
1340
1413
|
return this.preLoginMetricsBatcher.request(finalEvent);
|
|
@@ -390,6 +390,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
390
390
|
|
|
391
391
|
item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
|
|
392
392
|
|
|
393
|
+
// Mark call milestones in logs for easier filtering and analysis
|
|
394
|
+
if (eventName) {
|
|
395
|
+
webex.logger.log('Milestone,CallDiagnostic', eventName);
|
|
396
|
+
}
|
|
397
|
+
|
|
393
398
|
webex.logger.log(
|
|
394
399
|
`CallDiagnosticLatencies,prepareDiagnosticMetricItem: ${JSON.stringify({
|
|
395
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
package/src/metrics.types.ts
CHANGED
|
@@ -156,7 +156,6 @@ export type InternalEvent = {
|
|
|
156
156
|
| 'internal.register.device.request'
|
|
157
157
|
| 'internal.register.device.response'
|
|
158
158
|
| 'internal.reset.join.latencies'
|
|
159
|
-
| 'internal.client.meeting.click.joinbutton'
|
|
160
159
|
| 'internal.host.meeting.participant.admitted'
|
|
161
160
|
| 'internal.client.meeting.interstitial-window.showed'
|
|
162
161
|
| 'internal.client.interstitial-window.click.joinbutton'
|
|
@@ -319,6 +318,7 @@ export type PreComputedLatencies =
|
|
|
319
318
|
| 'internal.get.cluster.time'
|
|
320
319
|
| 'internal.click.to.interstitial'
|
|
321
320
|
| 'internal.click.to.interstitial.with.user.delay'
|
|
321
|
+
| 'internal.click.to.interstitial.for.client.jmt'
|
|
322
322
|
| 'internal.refresh.captcha.time'
|
|
323
323
|
| 'internal.exchange.ci.token.time'
|
|
324
324
|
| 'internal.get.u2c.time'
|
|
@@ -78,6 +78,8 @@ const PreLoginMetricsBatcher = Batcher.extend({
|
|
|
78
78
|
`PreLoginMetricsBatcher: @submitHttpRequest#${batchId}. Request successful.`
|
|
79
79
|
);
|
|
80
80
|
|
|
81
|
+
this.handleHttpResponseStatus(res?.statusCode, payload);
|
|
82
|
+
|
|
81
83
|
return res;
|
|
82
84
|
})
|
|
83
85
|
.catch((err) => {
|
|
@@ -87,9 +89,33 @@ const PreLoginMetricsBatcher = Batcher.extend({
|
|
|
87
89
|
`error: ${generateCommonErrorMetadata(err)}`
|
|
88
90
|
);
|
|
89
91
|
|
|
92
|
+
this.handleHttpResponseStatus(err?.statusCode, payload);
|
|
93
|
+
|
|
90
94
|
return Promise.reject(err);
|
|
91
95
|
});
|
|
92
96
|
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* React to the HTTP status code returned by the prelogin metrics endpoint.
|
|
100
|
+
* Only items submitted with `markTelemetryOptOutOnResponse: true` opt into
|
|
101
|
+
* this behavior.
|
|
102
|
+
* @param {number | undefined} statusCode
|
|
103
|
+
* @param {any[]} payload Items flushed in this HTTP batch.
|
|
104
|
+
* @returns {void}
|
|
105
|
+
*/
|
|
106
|
+
handleHttpResponseStatus(statusCode: number | undefined, payload: any[]) {
|
|
107
|
+
const shouldMark =
|
|
108
|
+
Array.isArray(payload) &&
|
|
109
|
+
payload.some((item) => item?.markTelemetryOptOutOnResponse === true);
|
|
110
|
+
|
|
111
|
+
if (!shouldMark) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (statusCode === 200) {
|
|
116
|
+
this.webex.internal.newMetrics?.callDiagnosticMetrics?.setIsTelemetryOptOutAutomatic(true);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
93
119
|
});
|
|
94
120
|
|
|
95
121
|
export default PreLoginMetricsBatcher;
|
|
@@ -177,6 +177,49 @@ describe('plugin-metrics', () => {
|
|
|
177
177
|
assert.lengthOf(webex.internal.metrics.batcher.queue, 0);
|
|
178
178
|
});
|
|
179
179
|
});
|
|
180
|
+
|
|
181
|
+
it('rejects the deferred without reenqueuing when batcherRetryOnNetworkError is false', () => {
|
|
182
|
+
webex.config.metrics = {...config.metrics, batcherRetryOnNetworkError: false};
|
|
183
|
+
|
|
184
|
+
webex.request = function () {
|
|
185
|
+
// noop
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
sinon.stub(webex, 'request').callsFake((options) => {
|
|
189
|
+
options.headers = {
|
|
190
|
+
trackingid: 'test-no-retry',
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
return Promise.reject(
|
|
194
|
+
new WebexHttpError.NetworkOrCORSError({
|
|
195
|
+
statusCode: 0,
|
|
196
|
+
options,
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const promise = webex.internal.metrics.batcher.request({
|
|
202
|
+
key: 'testMetric',
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return promiseTick(50)
|
|
206
|
+
.then(() => assert.lengthOf(webex.internal.metrics.batcher.queue, 1))
|
|
207
|
+
.then(() => clock.tick(config.metrics.batcherWait))
|
|
208
|
+
.then(() => assert.calledOnce(webex.request))
|
|
209
|
+
.then(() => promiseTick(50))
|
|
210
|
+
.then(() =>
|
|
211
|
+
promise.then(
|
|
212
|
+
() => {
|
|
213
|
+
assert.fail('promise should have been rejected');
|
|
214
|
+
},
|
|
215
|
+
(reason) => {
|
|
216
|
+
assert.instanceOf(reason, WebexHttpError.NetworkOrCORSError);
|
|
217
|
+
assert.lengthOf(webex.internal.metrics.batcher.queue, 0);
|
|
218
|
+
assert.calledOnce(webex.request);
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
});
|
|
180
223
|
});
|
|
181
224
|
});
|
|
182
225
|
});
|