@webex/plugin-meetings 3.3.1 → 3.4.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/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +7 -2
- package/dist/breakouts/index.js.map +1 -1
- package/dist/constants.js +11 -4
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/selfUtils.js +0 -5
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +70 -15
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/index.js +12 -0
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/connectionStateHandler.js +67 -0
- package/dist/meeting/connectionStateHandler.js.map +1 -0
- package/dist/meeting/index.js +552 -357
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +7 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +6 -1
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/index.js +4 -4
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -2
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/util.js +17 -17
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +16 -16
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +1 -1
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +37 -33
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +8 -0
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/util.js +3 -2
- package/dist/meetings/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +57 -0
- package/dist/metrics/index.js.map +1 -1
- package/dist/personal-meeting-room/index.js +1 -1
- package/dist/personal-meeting-room/index.js.map +1 -1
- package/dist/reachability/clusterReachability.js +108 -53
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +415 -56
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +11 -3
- package/dist/types/media/MediaConnectionAwaiter.d.ts +24 -4
- package/dist/types/meeting/connectionStateHandler.d.ts +30 -0
- package/dist/types/meeting/index.d.ts +27 -7
- package/dist/types/meeting/locusMediaRequest.d.ts +2 -0
- package/dist/types/meeting-info/index.d.ts +3 -2
- package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -2
- package/dist/types/meeting-info/util.d.ts +5 -4
- package/dist/types/meeting-info/utilv2.d.ts +3 -2
- package/dist/types/meetings/collection.d.ts +3 -2
- package/dist/types/meetings/index.d.ts +4 -3
- package/dist/types/meetings/meetings.types.d.ts +9 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/metrics/index.d.ts +15 -0
- package/dist/types/reachability/clusterReachability.d.ts +31 -3
- package/dist/types/reachability/index.d.ts +93 -2
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/breakouts/index.ts +7 -1
- package/src/constants.ts +13 -17
- package/src/locus-info/selfUtils.ts +0 -5
- package/src/media/MediaConnectionAwaiter.ts +89 -14
- package/src/media/index.ts +13 -0
- package/src/meeting/connectionStateHandler.ts +65 -0
- package/src/meeting/index.ts +526 -292
- package/src/meeting/locusMediaRequest.ts +5 -0
- package/src/meeting/muteState.ts +6 -1
- package/src/meeting/util.ts +1 -0
- package/src/meeting-info/index.ts +9 -6
- package/src/meeting-info/meeting-info-v2.ts +4 -4
- package/src/meeting-info/util.ts +23 -28
- package/src/meeting-info/utilv2.ts +18 -24
- package/src/meetings/collection.ts +3 -3
- package/src/meetings/index.ts +39 -40
- package/src/meetings/meetings.types.ts +11 -0
- package/src/meetings/util.ts +5 -4
- package/src/metrics/constants.ts +1 -0
- package/src/metrics/index.ts +44 -0
- package/src/personal-meeting-room/index.ts +2 -2
- package/src/reachability/clusterReachability.ts +86 -25
- package/src/reachability/index.ts +316 -27
- package/test/unit/spec/breakouts/index.ts +51 -32
- package/test/unit/spec/locus-info/selfUtils.js +25 -23
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +131 -32
- package/test/unit/spec/media/index.ts +42 -27
- package/test/unit/spec/meeting/connectionStateHandler.ts +102 -0
- package/test/unit/spec/meeting/index.js +758 -179
- package/test/unit/spec/meeting/locusMediaRequest.ts +7 -0
- package/test/unit/spec/meeting/muteState.js +24 -0
- package/test/unit/spec/meeting-info/index.js +4 -4
- package/test/unit/spec/meeting-info/meetinginfov2.js +24 -28
- package/test/unit/spec/meeting-info/request.js +2 -2
- package/test/unit/spec/meeting-info/utilv2.js +41 -49
- package/test/unit/spec/meetings/index.js +14 -0
- package/test/unit/spec/metrics/index.js +126 -0
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -2
- package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -2
- package/test/unit/spec/reachability/clusterReachability.ts +116 -22
- package/test/unit/spec/reachability/index.ts +1153 -84
- package/test/unit/spec/rtcMetrics/index.ts +1 -0
- package/dist/mediaQualityMetrics/config.js +0 -321
- package/dist/mediaQualityMetrics/config.js.map +0 -1
- package/dist/statsAnalyzer/global.js +0 -44
- package/dist/statsAnalyzer/global.js.map +0 -1
- package/dist/statsAnalyzer/index.js +0 -1072
- package/dist/statsAnalyzer/index.js.map +0 -1
- package/dist/statsAnalyzer/mqaUtil.js +0 -368
- package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +0 -247
- package/dist/types/statsAnalyzer/global.d.ts +0 -36
- package/dist/types/statsAnalyzer/index.d.ts +0 -217
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -48
- package/src/mediaQualityMetrics/config.ts +0 -255
- package/src/statsAnalyzer/global.ts +0 -37
- package/src/statsAnalyzer/index.ts +0 -1318
- package/src/statsAnalyzer/mqaUtil.ts +0 -463
- package/test/unit/spec/stats-analyzer/index.js +0 -1819
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/* eslint-disable class-methods-use-this */
|
|
6
|
-
import {mapValues} from 'lodash';
|
|
6
|
+
import {isEqual, mapValues, mean} from 'lodash';
|
|
7
7
|
|
|
8
|
+
import {Defer} from '@webex/common';
|
|
8
9
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
9
10
|
import MeetingUtil from '../meeting/util';
|
|
10
11
|
|
|
@@ -12,10 +13,16 @@ import {REACHABILITY} from '../constants';
|
|
|
12
13
|
|
|
13
14
|
import ReachabilityRequest, {ClusterList} from './request';
|
|
14
15
|
import {
|
|
16
|
+
ClientMediaIpsUpdatedEventData,
|
|
15
17
|
ClusterReachability,
|
|
16
18
|
ClusterReachabilityResult,
|
|
19
|
+
Events,
|
|
20
|
+
ResultEventData,
|
|
17
21
|
TransportResult,
|
|
18
22
|
} from './clusterReachability';
|
|
23
|
+
import EventsScope from '../common/events/events-scope';
|
|
24
|
+
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
25
|
+
import Metrics from '../metrics';
|
|
19
26
|
|
|
20
27
|
export type ReachabilityMetrics = {
|
|
21
28
|
reachability_public_udp_success: number;
|
|
@@ -60,11 +67,16 @@ export type ReachabilityResults = Record<
|
|
|
60
67
|
}
|
|
61
68
|
>;
|
|
62
69
|
|
|
70
|
+
// timeouts in seconds
|
|
71
|
+
const DEFAULT_TIMEOUT = 3;
|
|
72
|
+
const VIDEO_MESH_TIMEOUT = 1;
|
|
73
|
+
const OVERALL_TIMEOUT = 15;
|
|
74
|
+
|
|
63
75
|
/**
|
|
64
76
|
* @class Reachability
|
|
65
77
|
* @export
|
|
66
78
|
*/
|
|
67
|
-
export default class Reachability {
|
|
79
|
+
export default class Reachability extends EventsScope {
|
|
68
80
|
namespace = REACHABILITY.namespace;
|
|
69
81
|
webex: object;
|
|
70
82
|
reachabilityRequest: ReachabilityRequest;
|
|
@@ -72,12 +84,22 @@ export default class Reachability {
|
|
|
72
84
|
[key: string]: ClusterReachability;
|
|
73
85
|
};
|
|
74
86
|
|
|
87
|
+
reachabilityDefer?: Defer;
|
|
88
|
+
|
|
89
|
+
vmnTimer?: ReturnType<typeof setTimeout>;
|
|
90
|
+
publicCloudTimer?: ReturnType<typeof setTimeout>;
|
|
91
|
+
overallTimer?: ReturnType<typeof setTimeout>;
|
|
92
|
+
|
|
93
|
+
expectedResultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
|
94
|
+
resultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
|
95
|
+
|
|
75
96
|
/**
|
|
76
97
|
* Creates an instance of Reachability.
|
|
77
98
|
* @param {object} webex
|
|
78
99
|
* @memberof Reachability
|
|
79
100
|
*/
|
|
80
101
|
constructor(webex: object) {
|
|
102
|
+
super();
|
|
81
103
|
this.webex = webex;
|
|
82
104
|
|
|
83
105
|
/**
|
|
@@ -105,15 +127,6 @@ export default class Reachability {
|
|
|
105
127
|
MeetingUtil.getIpVersion(this.webex)
|
|
106
128
|
);
|
|
107
129
|
|
|
108
|
-
// Perform Reachability Check
|
|
109
|
-
const results = await this.performReachabilityChecks(clusters);
|
|
110
|
-
|
|
111
|
-
// @ts-ignore
|
|
112
|
-
await this.webex.boundedStorage.put(
|
|
113
|
-
this.namespace,
|
|
114
|
-
REACHABILITY.localStorageResult,
|
|
115
|
-
JSON.stringify(results)
|
|
116
|
-
);
|
|
117
130
|
// @ts-ignore
|
|
118
131
|
await this.webex.boundedStorage.put(
|
|
119
132
|
this.namespace,
|
|
@@ -121,11 +134,12 @@ export default class Reachability {
|
|
|
121
134
|
JSON.stringify(joinCookie)
|
|
122
135
|
);
|
|
123
136
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
this.reachabilityDefer = new Defer();
|
|
138
|
+
|
|
139
|
+
// Perform Reachability Check
|
|
140
|
+
await this.performReachabilityChecks(clusters);
|
|
127
141
|
|
|
128
|
-
return
|
|
142
|
+
return this.reachabilityDefer.promise;
|
|
129
143
|
} catch (error) {
|
|
130
144
|
LoggerProxy.logger.error(`Reachability:index#gatherReachability --> Error:`, error);
|
|
131
145
|
|
|
@@ -395,16 +409,210 @@ export default class Reachability {
|
|
|
395
409
|
});
|
|
396
410
|
}
|
|
397
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Returns true if we've obtained all the reachability results for all the public clusters
|
|
414
|
+
* In other words, it means that all public clusters are reachable over each protocol,
|
|
415
|
+
* because we only get a "result" if we managed to reach a cluster
|
|
416
|
+
*
|
|
417
|
+
* @returns {boolean}
|
|
418
|
+
*/
|
|
419
|
+
private areAllPublicClusterResultsReady() {
|
|
420
|
+
return isEqual(this.expectedResultsCount.public, this.resultsCount.public);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Returns true if we've obtained all the reachability results for all the clusters
|
|
425
|
+
*
|
|
426
|
+
* @returns {boolean}
|
|
427
|
+
*/
|
|
428
|
+
private areAllResultsReady() {
|
|
429
|
+
return isEqual(this.expectedResultsCount, this.resultsCount);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Resolves the promise returned by gatherReachability() method
|
|
434
|
+
* @returns {void}
|
|
435
|
+
*/
|
|
436
|
+
private resolveReachabilityPromise() {
|
|
437
|
+
if (this.vmnTimer) {
|
|
438
|
+
clearTimeout(this.vmnTimer);
|
|
439
|
+
}
|
|
440
|
+
if (this.publicCloudTimer) {
|
|
441
|
+
clearTimeout(this.publicCloudTimer);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
this.logUnreachableClusters();
|
|
445
|
+
this.reachabilityDefer?.resolve();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Aborts all cluster reachability checks that are in progress
|
|
450
|
+
*
|
|
451
|
+
* @returns {void}
|
|
452
|
+
*/
|
|
453
|
+
private abortClusterReachability() {
|
|
454
|
+
Object.values(this.clusterReachability).forEach((clusterReachability) => {
|
|
455
|
+
clusterReachability.abort();
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Helper function for calculating min/max/average values of latency
|
|
461
|
+
*
|
|
462
|
+
* @param {Array<any>} results
|
|
463
|
+
* @param {string} protocol
|
|
464
|
+
* @param {boolean} isVideoMesh
|
|
465
|
+
* @returns {{min:number, max: number, average: number}}
|
|
466
|
+
*/
|
|
467
|
+
protected getStatistics(
|
|
468
|
+
results: Array<ClusterReachabilityResult & {isVideoMesh: boolean}>,
|
|
469
|
+
protocol: 'udp' | 'tcp' | 'xtls',
|
|
470
|
+
isVideoMesh: boolean
|
|
471
|
+
) {
|
|
472
|
+
const values = results
|
|
473
|
+
.filter((result) => result.isVideoMesh === isVideoMesh)
|
|
474
|
+
.filter((result) => result[protocol].result === 'reachable')
|
|
475
|
+
.map((result) => result[protocol].latencyInMilliseconds);
|
|
476
|
+
|
|
477
|
+
if (values.length === 0) {
|
|
478
|
+
return {
|
|
479
|
+
min: -1,
|
|
480
|
+
max: -1,
|
|
481
|
+
average: -1,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
min: Math.min(...values),
|
|
487
|
+
max: Math.max(...values),
|
|
488
|
+
average: mean(values),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Sends a metric with all the statistics about how long reachability took
|
|
494
|
+
*
|
|
495
|
+
* @returns {void}
|
|
496
|
+
*/
|
|
497
|
+
protected async sendMetric() {
|
|
498
|
+
const results = [];
|
|
499
|
+
|
|
500
|
+
Object.values(this.clusterReachability).forEach((clusterReachability) => {
|
|
501
|
+
results.push({
|
|
502
|
+
...clusterReachability.getResult(),
|
|
503
|
+
isVideoMesh: clusterReachability.isVideoMesh,
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
const stats = {
|
|
508
|
+
vmn: {
|
|
509
|
+
udp: this.getStatistics(results, 'udp', true),
|
|
510
|
+
},
|
|
511
|
+
public: {
|
|
512
|
+
udp: this.getStatistics(results, 'udp', false),
|
|
513
|
+
tcp: this.getStatistics(results, 'tcp', false),
|
|
514
|
+
xtls: this.getStatistics(results, 'xtls', false),
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
Metrics.sendBehavioralMetric(
|
|
518
|
+
BEHAVIORAL_METRICS.REACHABILITY_COMPLETED,
|
|
519
|
+
Metrics.prepareMetricFields(stats)
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Starts all the timers used for various timeouts
|
|
525
|
+
*
|
|
526
|
+
* @returns {void}
|
|
527
|
+
*/
|
|
528
|
+
private startTimers() {
|
|
529
|
+
this.vmnTimer = setTimeout(() => {
|
|
530
|
+
this.vmnTimer = undefined;
|
|
531
|
+
// if we are only missing VMN results, then we don't want to wait for them any longer
|
|
532
|
+
// as they are likely to fail if users are not on corporate network
|
|
533
|
+
if (this.areAllPublicClusterResultsReady()) {
|
|
534
|
+
LoggerProxy.logger.log(
|
|
535
|
+
'Reachability:index#startTimers --> Reachability checks timed out (VMN timeout)'
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
this.resolveReachabilityPromise();
|
|
539
|
+
}
|
|
540
|
+
}, VIDEO_MESH_TIMEOUT * 1000);
|
|
541
|
+
|
|
542
|
+
this.publicCloudTimer = setTimeout(() => {
|
|
543
|
+
this.publicCloudTimer = undefined;
|
|
544
|
+
|
|
545
|
+
LoggerProxy.logger.log(
|
|
546
|
+
`Reachability:index#startTimers --> Reachability checks timed out (${DEFAULT_TIMEOUT}s)`
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
// resolve the promise, so that the client won't be blocked waiting on meetings.register() for too long
|
|
550
|
+
this.resolveReachabilityPromise();
|
|
551
|
+
}, DEFAULT_TIMEOUT * 1000);
|
|
552
|
+
|
|
553
|
+
this.overallTimer = setTimeout(() => {
|
|
554
|
+
this.overallTimer = undefined;
|
|
555
|
+
this.abortClusterReachability();
|
|
556
|
+
this.emit(
|
|
557
|
+
{
|
|
558
|
+
file: 'reachability',
|
|
559
|
+
function: 'overallTimer timeout',
|
|
560
|
+
},
|
|
561
|
+
'reachability:done',
|
|
562
|
+
{}
|
|
563
|
+
);
|
|
564
|
+
this.sendMetric();
|
|
565
|
+
|
|
566
|
+
LoggerProxy.logger.log(
|
|
567
|
+
`Reachability:index#startTimers --> Reachability checks fully timed out (${OVERALL_TIMEOUT}s)`
|
|
568
|
+
);
|
|
569
|
+
}, OVERALL_TIMEOUT * 1000);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Stores given reachability results in local storage
|
|
574
|
+
*
|
|
575
|
+
* @param {ReachabilityResults} results
|
|
576
|
+
* @returns {Promise<void>}
|
|
577
|
+
*/
|
|
578
|
+
private async storeResults(results: ReachabilityResults) {
|
|
579
|
+
// @ts-ignore
|
|
580
|
+
await this.webex.boundedStorage.put(
|
|
581
|
+
this.namespace,
|
|
582
|
+
REACHABILITY.localStorageResult,
|
|
583
|
+
JSON.stringify(results)
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Resets all the internal counters that keep track of the results
|
|
589
|
+
*
|
|
590
|
+
* @returns {void}
|
|
591
|
+
*/
|
|
592
|
+
private resetResultCounters() {
|
|
593
|
+
this.expectedResultsCount.videoMesh.udp = 0;
|
|
594
|
+
this.expectedResultsCount.public.udp = 0;
|
|
595
|
+
this.expectedResultsCount.public.tcp = 0;
|
|
596
|
+
this.expectedResultsCount.public.xtls = 0;
|
|
597
|
+
|
|
598
|
+
this.resultsCount.videoMesh.udp = 0;
|
|
599
|
+
this.resultsCount.public.udp = 0;
|
|
600
|
+
this.resultsCount.public.tcp = 0;
|
|
601
|
+
this.resultsCount.public.xtls = 0;
|
|
602
|
+
}
|
|
603
|
+
|
|
398
604
|
/**
|
|
399
605
|
* Performs reachability checks for all clusters
|
|
400
606
|
* @param {ClusterList} clusterList
|
|
401
|
-
* @returns {Promise<
|
|
607
|
+
* @returns {Promise<void>} promise that's resolved as soon as the checks are started
|
|
402
608
|
*/
|
|
403
|
-
private async performReachabilityChecks(clusterList: ClusterList)
|
|
609
|
+
private async performReachabilityChecks(clusterList: ClusterList) {
|
|
404
610
|
const results: ReachabilityResults = {};
|
|
405
611
|
|
|
612
|
+
this.clusterReachability = {};
|
|
613
|
+
|
|
406
614
|
if (!clusterList || !Object.keys(clusterList).length) {
|
|
407
|
-
return
|
|
615
|
+
return;
|
|
408
616
|
}
|
|
409
617
|
|
|
410
618
|
LoggerProxy.logger.log(
|
|
@@ -417,7 +625,11 @@ export default class Reachability {
|
|
|
417
625
|
} reachability checks`
|
|
418
626
|
);
|
|
419
627
|
|
|
420
|
-
|
|
628
|
+
this.resetResultCounters();
|
|
629
|
+
this.startTimers();
|
|
630
|
+
|
|
631
|
+
// sanitize the urls in the clusterList
|
|
632
|
+
Object.keys(clusterList).forEach((key) => {
|
|
421
633
|
const cluster = clusterList[key];
|
|
422
634
|
|
|
423
635
|
// Linus doesn't support TCP reachability checks on video mesh nodes
|
|
@@ -429,6 +641,7 @@ export default class Reachability {
|
|
|
429
641
|
cluster.tcp = [];
|
|
430
642
|
}
|
|
431
643
|
|
|
644
|
+
// Linus doesn't support xTLS reachability checks on video mesh nodes
|
|
432
645
|
const includeTlsReachability =
|
|
433
646
|
// @ts-ignore
|
|
434
647
|
this.webex.config.meetings.experimental.enableTlsReachability && !cluster.isVideoMesh;
|
|
@@ -437,18 +650,94 @@ export default class Reachability {
|
|
|
437
650
|
cluster.xtls = [];
|
|
438
651
|
}
|
|
439
652
|
|
|
440
|
-
|
|
653
|
+
// initialize the result for this cluster
|
|
654
|
+
results[key] = {
|
|
655
|
+
udp: {result: cluster.udp.length > 0 ? 'unreachable' : 'untested'},
|
|
656
|
+
tcp: {result: cluster.tcp.length > 0 ? 'unreachable' : 'untested'},
|
|
657
|
+
xtls: {result: cluster.xtls.length > 0 ? 'unreachable' : 'untested'},
|
|
658
|
+
isVideoMesh: cluster.isVideoMesh,
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
// update expected results counters to include this cluster
|
|
662
|
+
this.expectedResultsCount[cluster.isVideoMesh ? 'videoMesh' : 'public'].udp +=
|
|
663
|
+
cluster.udp.length;
|
|
664
|
+
if (!cluster.isVideoMesh) {
|
|
665
|
+
this.expectedResultsCount.public.tcp += cluster.tcp.length;
|
|
666
|
+
this.expectedResultsCount.public.xtls += cluster.xtls.length;
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
const isFirstResult = {
|
|
671
|
+
udp: true,
|
|
672
|
+
tcp: true,
|
|
673
|
+
xtls: true,
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
// save the initialized results (in case we don't get any "resultReady" events at all)
|
|
677
|
+
await this.storeResults(results);
|
|
441
678
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
679
|
+
// now start the reachability on all the clusters
|
|
680
|
+
Object.keys(clusterList).forEach((key) => {
|
|
681
|
+
const cluster = clusterList[key];
|
|
682
|
+
|
|
683
|
+
this.clusterReachability[key] = new ClusterReachability(key, cluster);
|
|
684
|
+
this.clusterReachability[key].on(Events.resultReady, async (data: ResultEventData) => {
|
|
685
|
+
const {protocol, result, clientMediaIPs, latencyInMilliseconds} = data;
|
|
686
|
+
|
|
687
|
+
if (isFirstResult[protocol]) {
|
|
688
|
+
this.emit(
|
|
689
|
+
{
|
|
690
|
+
file: 'reachability',
|
|
691
|
+
function: 'resultReady event handler',
|
|
692
|
+
},
|
|
693
|
+
'reachability:firstResultAvailable',
|
|
694
|
+
{
|
|
695
|
+
protocol,
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
isFirstResult[protocol] = false;
|
|
699
|
+
}
|
|
700
|
+
this.resultsCount[cluster.isVideoMesh ? 'videoMesh' : 'public'][protocol] += 1;
|
|
701
|
+
|
|
702
|
+
const areAllResultsReady = this.areAllResultsReady();
|
|
703
|
+
|
|
704
|
+
results[key][protocol].result = result;
|
|
705
|
+
results[key][protocol].clientMediaIPs = clientMediaIPs;
|
|
706
|
+
results[key][protocol].latencyInMilliseconds = latencyInMilliseconds;
|
|
707
|
+
|
|
708
|
+
await this.storeResults(results);
|
|
709
|
+
|
|
710
|
+
if (areAllResultsReady) {
|
|
711
|
+
clearTimeout(this.overallTimer);
|
|
712
|
+
this.overallTimer = undefined;
|
|
713
|
+
this.emit(
|
|
714
|
+
{
|
|
715
|
+
file: 'reachability',
|
|
716
|
+
function: 'performReachabilityChecks',
|
|
717
|
+
},
|
|
718
|
+
'reachability:done',
|
|
719
|
+
{}
|
|
720
|
+
);
|
|
721
|
+
this.sendMetric();
|
|
722
|
+
|
|
723
|
+
LoggerProxy.logger.log(
|
|
724
|
+
`Reachability:index#gatherReachability --> Reachability checks fully completed`
|
|
725
|
+
);
|
|
726
|
+
this.resolveReachabilityPromise();
|
|
727
|
+
}
|
|
445
728
|
});
|
|
446
|
-
});
|
|
447
729
|
|
|
448
|
-
|
|
730
|
+
// clientMediaIps can be updated independently from the results, so we need to listen for them too
|
|
731
|
+
this.clusterReachability[key].on(
|
|
732
|
+
Events.clientMediaIpsUpdated,
|
|
733
|
+
async (data: ClientMediaIpsUpdatedEventData) => {
|
|
734
|
+
results[key][data.protocol].clientMediaIPs = data.clientMediaIPs;
|
|
449
735
|
|
|
450
|
-
|
|
736
|
+
await this.storeResults(results);
|
|
737
|
+
}
|
|
738
|
+
);
|
|
451
739
|
|
|
452
|
-
|
|
740
|
+
this.clusterReachability[key].start(); // not awaiting on purpose
|
|
741
|
+
});
|
|
453
742
|
}
|
|
454
743
|
}
|
|
@@ -87,6 +87,7 @@ describe('plugin-meetings', () => {
|
|
|
87
87
|
// @ts-ignore
|
|
88
88
|
webex = new MockWebex({});
|
|
89
89
|
webex.internal.llm.on = sinon.stub();
|
|
90
|
+
webex.internal.llm.isConnected = sinon.stub();
|
|
90
91
|
webex.internal.mercury.on = sinon.stub();
|
|
91
92
|
breakouts = new Breakouts({}, {parent: webex});
|
|
92
93
|
breakouts.groupId = 'groupId';
|
|
@@ -225,38 +226,6 @@ describe('plugin-meetings', () => {
|
|
|
225
226
|
});
|
|
226
227
|
});
|
|
227
228
|
|
|
228
|
-
describe('#listenToBroadcastMessages', () => {
|
|
229
|
-
it('triggers message event when a message received', () => {
|
|
230
|
-
const call = webex.internal.llm.on.getCall(0);
|
|
231
|
-
const callback = call.args[1];
|
|
232
|
-
|
|
233
|
-
assert.equal(call.args[0], 'event:breakout.message');
|
|
234
|
-
|
|
235
|
-
let message;
|
|
236
|
-
|
|
237
|
-
breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MESSAGE, (event) => {
|
|
238
|
-
message = event;
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
breakouts.currentBreakoutSession.sessionId = 'sessionId';
|
|
242
|
-
|
|
243
|
-
callback({
|
|
244
|
-
data: {
|
|
245
|
-
senderUserId: 'senderUserId',
|
|
246
|
-
sentTime: 'sentTime',
|
|
247
|
-
message: 'message',
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
assert.deepEqual(message, {
|
|
252
|
-
senderUserId: 'senderUserId',
|
|
253
|
-
sentTime: 'sentTime',
|
|
254
|
-
message: 'message',
|
|
255
|
-
sessionId: 'sessionId',
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
229
|
describe('#listenToBreakoutRosters', () => {
|
|
261
230
|
it('triggers member update event when a roster received', () => {
|
|
262
231
|
const call = webex.internal.mercury.on.getCall(0);
|
|
@@ -496,8 +465,58 @@ describe('plugin-meetings', () => {
|
|
|
496
465
|
describe('#locusUrlUpdate', () => {
|
|
497
466
|
it('sets the locus url', () => {
|
|
498
467
|
breakouts.locusUrlUpdate('newUrl');
|
|
468
|
+
assert.equal(breakouts.locusUrl, 'newUrl');
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
describe('#listenToBroadcastMessages', () => {
|
|
473
|
+
it('do not subscribe message if llm not connected', () => {
|
|
474
|
+
webex.internal.llm.isConnected = sinon.stub().returns(false);
|
|
475
|
+
breakouts.listenTo = sinon.stub();
|
|
476
|
+
breakouts.locusUrlUpdate('newUrl');
|
|
477
|
+
assert.equal(breakouts.locusUrl, 'newUrl');
|
|
478
|
+
assert.notCalled(breakouts.listenTo);
|
|
479
|
+
});
|
|
499
480
|
|
|
481
|
+
it('do not subscribe message if already done', () => {
|
|
482
|
+
webex.internal.llm.isConnected = sinon.stub().returns(true);
|
|
483
|
+
breakouts.hasSubscribedToMessage = true;
|
|
484
|
+
breakouts.listenTo = sinon.stub();
|
|
485
|
+
breakouts.locusUrlUpdate('newUrl');
|
|
500
486
|
assert.equal(breakouts.locusUrl, 'newUrl');
|
|
487
|
+
assert.notCalled(breakouts.listenTo);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('triggers message event when a message received', () => {
|
|
491
|
+
webex.internal.llm.isConnected = sinon.stub().returns(true);
|
|
492
|
+
breakouts.locusUrlUpdate('newUrl');
|
|
493
|
+
const call = webex.internal.llm.on.getCall(0);
|
|
494
|
+
const callback = call.args[1];
|
|
495
|
+
|
|
496
|
+
assert.equal(call.args[0], 'event:breakout.message');
|
|
497
|
+
|
|
498
|
+
let message;
|
|
499
|
+
|
|
500
|
+
breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MESSAGE, (event) => {
|
|
501
|
+
message = event;
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
breakouts.currentBreakoutSession.sessionId = 'sessionId';
|
|
505
|
+
|
|
506
|
+
callback({
|
|
507
|
+
data: {
|
|
508
|
+
senderUserId: 'senderUserId',
|
|
509
|
+
sentTime: 'sentTime',
|
|
510
|
+
message: 'message',
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
assert.deepEqual(message, {
|
|
515
|
+
senderUserId: 'senderUserId',
|
|
516
|
+
sentTime: 'sentTime',
|
|
517
|
+
message: 'message',
|
|
518
|
+
sessionId: 'sessionId',
|
|
519
|
+
});
|
|
501
520
|
});
|
|
502
521
|
});
|
|
503
522
|
|
|
@@ -345,37 +345,39 @@ describe('plugin-meetings', () => {
|
|
|
345
345
|
});
|
|
346
346
|
|
|
347
347
|
describe('mutedByOthersChanged', () => {
|
|
348
|
-
it('throws an error if changedSelf is not provided', function() {
|
|
349
|
-
assert.throws(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
assert.equal(SelfUtils.mutedByOthersChanged(null, { remoteMuted: false }), false);
|
|
348
|
+
it('throws an error if changedSelf is not provided', function () {
|
|
349
|
+
assert.throws(
|
|
350
|
+
() => SelfUtils.mutedByOthersChanged({}, null),
|
|
351
|
+
'New self must be defined to determine if self was muted by others.'
|
|
352
|
+
);
|
|
354
353
|
});
|
|
355
354
|
|
|
356
|
-
it('
|
|
357
|
-
assert.equal(SelfUtils.mutedByOthersChanged(null, {
|
|
355
|
+
it('return false when oldSelf is not defined', function () {
|
|
356
|
+
assert.equal(SelfUtils.mutedByOthersChanged(null, {remoteMuted: false}), false);
|
|
358
357
|
});
|
|
359
358
|
|
|
360
|
-
it('should return
|
|
361
|
-
assert.equal(SelfUtils.mutedByOthersChanged(
|
|
362
|
-
{ remoteMuted: false },
|
|
363
|
-
{ remoteMuted: true, selfIdentity: 'user1', modifiedBy: 'user1' }
|
|
364
|
-
), false);
|
|
359
|
+
it('should return true when remoteMuted is true on entry', function () {
|
|
360
|
+
assert.equal(SelfUtils.mutedByOthersChanged(null, {remoteMuted: true}), true);
|
|
365
361
|
});
|
|
366
362
|
|
|
367
|
-
it('should return true when remoteMuted values are different', function() {
|
|
368
|
-
assert.equal(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
363
|
+
it('should return true when remoteMuted values are different', function () {
|
|
364
|
+
assert.equal(
|
|
365
|
+
SelfUtils.mutedByOthersChanged(
|
|
366
|
+
{remoteMuted: false},
|
|
367
|
+
{remoteMuted: true, selfIdentity: 'user1', modifiedBy: 'user2'}
|
|
368
|
+
),
|
|
369
|
+
true
|
|
370
|
+
);
|
|
372
371
|
});
|
|
373
372
|
|
|
374
|
-
it('should return true when remoteMuted is true and unmuteAllowed has changed', function() {
|
|
375
|
-
assert.equal(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
373
|
+
it('should return true when remoteMuted is true and unmuteAllowed has changed', function () {
|
|
374
|
+
assert.equal(
|
|
375
|
+
SelfUtils.mutedByOthersChanged(
|
|
376
|
+
{remoteMuted: true, unmuteAllowed: false},
|
|
377
|
+
{remoteMuted: true, unmuteAllowed: true, selfIdentity: 'user1', modifiedBy: 'user2'}
|
|
378
|
+
),
|
|
379
|
+
true
|
|
380
|
+
);
|
|
379
381
|
});
|
|
380
382
|
});
|
|
381
383
|
|