@webex/internal-plugin-metrics 3.12.0-next.3 → 3.12.0-next.30
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 +59 -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 +39 -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 +51 -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 +689 -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,37 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
145
147
|
return null;
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Returns the telemetryOptOut value of the current user
|
|
152
|
+
* @returns one of 'manual', 'automatic', undefined
|
|
153
|
+
*/
|
|
154
|
+
public getTelemetryOptOut() {
|
|
155
|
+
if (this.isTelemetryOptOutManual) {
|
|
156
|
+
return 'manual';
|
|
157
|
+
}
|
|
158
|
+
if (this.isTelemetryOptOutAutomatic) {
|
|
159
|
+
return 'automatic';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Sets the manual telemetry opt-out status for the current user
|
|
167
|
+
* @param value - boolean value indicating manual telemetry opt-out status
|
|
168
|
+
*/
|
|
169
|
+
public setIsTelemetryOptOutManual(value: boolean) {
|
|
170
|
+
this.isTelemetryOptOutManual = value;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Sets the automatic telemetry opt-out status for the current user
|
|
175
|
+
* @param value - boolean value indicating automatic telemetry opt-out status
|
|
176
|
+
*/
|
|
177
|
+
public setIsTelemetryOptOutAutomatic(value: boolean) {
|
|
178
|
+
this.isTelemetryOptOutAutomatic = value;
|
|
179
|
+
}
|
|
180
|
+
|
|
148
181
|
/**
|
|
149
182
|
* Returns if the meeting has converged architecture enabled
|
|
150
183
|
* @param options.meetingId
|
|
@@ -190,6 +223,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
190
223
|
|
|
191
224
|
// if ConvergedArchitecture enable and isConvergedWebinarWebcast -- then webcast
|
|
192
225
|
if (meetingInfo?.enableConvergedArchitecture && meetingInfo?.enableEvent) {
|
|
226
|
+
// if enableConvergedWebinarLargeScale - then large scale webinar
|
|
227
|
+
if (meetingInfo?.enableConvergedWebinarLargeScale) {
|
|
228
|
+
return WEBEX_SUB_SERVICE_TYPES.LARGE_SCALE_WEBINAR;
|
|
229
|
+
}
|
|
230
|
+
|
|
193
231
|
return meetingInfo?.isConvergedWebinarWebcast
|
|
194
232
|
? WEBEX_SUB_SERVICE_TYPES.WEBCAST
|
|
195
233
|
: WEBEX_SUB_SERVICE_TYPES.WEBINAR;
|
|
@@ -602,6 +640,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
602
640
|
mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
|
|
603
641
|
startTime: new Date().toISOString(),
|
|
604
642
|
},
|
|
643
|
+
webexSubServiceType: this.getSubServiceType(meeting),
|
|
605
644
|
};
|
|
606
645
|
|
|
607
646
|
// merge any new properties, or override existing ones
|
|
@@ -984,7 +1023,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
984
1023
|
sessionCorrelationId,
|
|
985
1024
|
});
|
|
986
1025
|
|
|
987
|
-
// create common event object
|
|
1026
|
+
// create common event object structure
|
|
988
1027
|
const commonEventObject = {
|
|
989
1028
|
name,
|
|
990
1029
|
canProceed: true,
|
|
@@ -997,6 +1036,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
997
1036
|
'loginType' in meeting.callStateForMetrics
|
|
998
1037
|
? meeting.callStateForMetrics.loginType
|
|
999
1038
|
: this.getCurLoginType(),
|
|
1039
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1000
1040
|
isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
|
|
1001
1041
|
meetingId,
|
|
1002
1042
|
}),
|
|
@@ -1005,6 +1045,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1005
1045
|
webexSubServiceType: this.getSubServiceType(meeting),
|
|
1006
1046
|
// @ts-ignore
|
|
1007
1047
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1048
|
+
isVipMeeting: meeting?.meetingInfo?.vipmeeting || false,
|
|
1049
|
+
isAutomatedUser:
|
|
1050
|
+
typeof window !== 'undefined' && typeof navigator !== 'undefined' && !!navigator?.webdriver, // if webdriver is true, it's most likely in a test environment
|
|
1008
1051
|
};
|
|
1009
1052
|
|
|
1010
1053
|
const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
|
|
@@ -1124,8 +1167,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1124
1167
|
isMercuryConnected: this.isMercuryConnected,
|
|
1125
1168
|
},
|
|
1126
1169
|
loginType: this.getCurLoginType(),
|
|
1170
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1127
1171
|
// @ts-ignore
|
|
1128
1172
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1173
|
+
isAutomatedUser:
|
|
1174
|
+
typeof window !== 'undefined' && typeof navigator !== 'undefined' && !!navigator?.webdriver, // if webdriver is true, it's most likely in a test environment
|
|
1129
1175
|
};
|
|
1130
1176
|
|
|
1131
1177
|
if (options.joinFlowVersion) {
|
|
@@ -1318,6 +1364,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1318
1364
|
const finalEvent = {
|
|
1319
1365
|
eventPayload: event,
|
|
1320
1366
|
type: ['diagnostic-event'],
|
|
1367
|
+
markTelemetryOptOutOnResponse: true,
|
|
1321
1368
|
};
|
|
1322
1369
|
|
|
1323
1370
|
return this.callDiagnosticEventsBatcher.request(finalEvent);
|
|
@@ -1327,6 +1374,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1327
1374
|
* Prepare the event and send the request to metrics-a service, pre login.
|
|
1328
1375
|
* @param event
|
|
1329
1376
|
* @param preLoginId
|
|
1377
|
+
* @param markTelemetryOptOutOnResponse
|
|
1330
1378
|
* @returns
|
|
1331
1379
|
*/
|
|
1332
1380
|
submitToCallDiagnosticsPreLogin = (event: Event, preLoginId?: string): Promise<any> => {
|
|
@@ -1334,7 +1382,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1334
1382
|
const finalEvent = {
|
|
1335
1383
|
eventPayload: event,
|
|
1336
1384
|
type: ['diagnostic-event'],
|
|
1385
|
+
markTelemetryOptOutOnResponse: true,
|
|
1337
1386
|
};
|
|
1387
|
+
|
|
1338
1388
|
this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
|
|
1339
1389
|
|
|
1340
1390
|
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
|
});
|