@webex/plugin-meetings 3.8.0-next.3 → 3.8.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/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +4 -4
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +1 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/meeting/index.js +89 -5
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +21 -5
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/util.js +4 -1
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +359 -60
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +60 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +3 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +9 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +52 -8
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +70 -45
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/request.js +19 -3
- package/dist/reachability/request.js.map +1 -1
- package/dist/recording-controller/util.js +5 -5
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/meeting/index.d.ts +30 -0
- package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
- package/dist/types/meetings/index.d.ts +29 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +9 -0
- package/dist/types/reachability/clusterReachability.d.ts +13 -1
- package/dist/types/reachability/index.d.ts +2 -1
- package/dist/types/reachability/reachability.types.d.ts +5 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/config.ts +1 -0
- package/src/constants.ts +1 -0
- package/src/interpretation/index.ts +3 -3
- package/src/locus-info/controlsUtils.ts +2 -2
- package/src/meeting/index.ts +85 -7
- package/src/meeting/locusMediaRequest.ts +27 -4
- package/src/meeting/util.ts +2 -1
- package/src/meeting-info/meeting-info-v2.ts +247 -6
- package/src/meetings/index.ts +72 -1
- package/src/member/index.ts +11 -0
- package/src/member/util.ts +3 -0
- package/src/metrics/constants.ts +9 -0
- package/src/reachability/clusterReachability.ts +47 -1
- package/src/reachability/index.ts +15 -0
- package/src/reachability/reachability.types.ts +6 -0
- package/src/reachability/request.ts +7 -0
- package/src/recording-controller/util.ts +17 -13
- package/test/unit/spec/interpretation/index.ts +39 -1
- package/test/unit/spec/locus-info/controlsUtils.js +8 -0
- package/test/unit/spec/meeting/index.js +200 -108
- package/test/unit/spec/meeting/locusMediaRequest.ts +96 -58
- package/test/unit/spec/meeting/utils.js +55 -0
- package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
- package/test/unit/spec/meetings/index.js +78 -1
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/reachability/clusterReachability.ts +47 -1
- package/test/unit/spec/reachability/index.ts +12 -0
- package/test/unit/spec/reachability/request.js +47 -2
|
@@ -6,7 +6,7 @@ import {convertStunUrlToTurn, convertStunUrlToTurnTls} from './util';
|
|
|
6
6
|
import EventsScope from '../common/events/events-scope';
|
|
7
7
|
|
|
8
8
|
import {CONNECTION_STATE, Enum, ICE_GATHERING_STATE} from '../constants';
|
|
9
|
-
import {ClusterReachabilityResult} from './reachability.types';
|
|
9
|
+
import {ClusterReachabilityResult, NatType} from './reachability.types';
|
|
10
10
|
|
|
11
11
|
// data for the Events.resultReady event
|
|
12
12
|
export type ResultEventData = {
|
|
@@ -22,9 +22,14 @@ export type ClientMediaIpsUpdatedEventData = {
|
|
|
22
22
|
clientMediaIPs: string[];
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
export type NatTypeUpdatedEventData = {
|
|
26
|
+
natType: NatType;
|
|
27
|
+
};
|
|
28
|
+
|
|
25
29
|
export const Events = {
|
|
26
30
|
resultReady: 'resultReady', // emitted when a cluster is reached successfully using specific protocol
|
|
27
31
|
clientMediaIpsUpdated: 'clientMediaIpsUpdated', // emitted when more public IPs are found after resultReady was already sent for a given protocol
|
|
32
|
+
natTypeUpdated: 'natTypeUpdated', // emitted when NAT type is determined
|
|
28
33
|
} as const;
|
|
29
34
|
|
|
30
35
|
export type Events = Enum<typeof Events>;
|
|
@@ -41,6 +46,7 @@ export class ClusterReachability extends EventsScope {
|
|
|
41
46
|
private pc?: RTCPeerConnection;
|
|
42
47
|
private defer: Defer; // this defer is resolved once reachability checks for this cluster are completed
|
|
43
48
|
private startTimestamp: number;
|
|
49
|
+
private srflxIceCandidates: RTCIceCandidate[] = [];
|
|
44
50
|
public readonly isVideoMesh: boolean;
|
|
45
51
|
public readonly name;
|
|
46
52
|
|
|
@@ -290,6 +296,44 @@ export class ClusterReachability extends EventsScope {
|
|
|
290
296
|
}
|
|
291
297
|
}
|
|
292
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Determines NAT Type.
|
|
301
|
+
*
|
|
302
|
+
* @param {RTCIceCandidate} candidate
|
|
303
|
+
* @returns {void}
|
|
304
|
+
*/
|
|
305
|
+
private determineNatType(candidate: RTCIceCandidate) {
|
|
306
|
+
this.srflxIceCandidates.push(candidate);
|
|
307
|
+
|
|
308
|
+
if (this.srflxIceCandidates.length > 1) {
|
|
309
|
+
const portsFound: Record<string, Set<number>> = {};
|
|
310
|
+
|
|
311
|
+
this.srflxIceCandidates.forEach((c) => {
|
|
312
|
+
const key = `${c.address}:${c.relatedPort}`;
|
|
313
|
+
if (!portsFound[key]) {
|
|
314
|
+
portsFound[key] = new Set();
|
|
315
|
+
}
|
|
316
|
+
portsFound[key].add(c.port);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
Object.entries(portsFound).forEach(([, ports]) => {
|
|
320
|
+
if (ports.size > 1) {
|
|
321
|
+
// Found candidates with the same address and relatedPort, but different ports
|
|
322
|
+
this.emit(
|
|
323
|
+
{
|
|
324
|
+
file: 'clusterReachability',
|
|
325
|
+
function: 'determineNatType',
|
|
326
|
+
},
|
|
327
|
+
Events.natTypeUpdated,
|
|
328
|
+
{
|
|
329
|
+
natType: NatType.SymmetricNat,
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
293
337
|
/**
|
|
294
338
|
* Registers a listener for the icecandidate event
|
|
295
339
|
*
|
|
@@ -308,6 +352,8 @@ export class ClusterReachability extends EventsScope {
|
|
|
308
352
|
if (e.candidate) {
|
|
309
353
|
if (e.candidate.type === CANDIDATE_TYPES.SERVER_REFLEXIVE) {
|
|
310
354
|
this.saveResult('udp', latencyInMilliseconds, e.candidate.address);
|
|
355
|
+
|
|
356
|
+
this.determineNatType(e.candidate);
|
|
311
357
|
}
|
|
312
358
|
|
|
313
359
|
if (e.candidate.type === CANDIDATE_TYPES.RELAY) {
|
|
@@ -23,11 +23,13 @@ import {
|
|
|
23
23
|
ReachabilityResultsForBackend,
|
|
24
24
|
TransportResultForBackend,
|
|
25
25
|
GetClustersTrigger,
|
|
26
|
+
NatType,
|
|
26
27
|
} from './reachability.types';
|
|
27
28
|
import {
|
|
28
29
|
ClientMediaIpsUpdatedEventData,
|
|
29
30
|
ClusterReachability,
|
|
30
31
|
Events,
|
|
32
|
+
NatTypeUpdatedEventData,
|
|
31
33
|
ResultEventData,
|
|
32
34
|
} from './clusterReachability';
|
|
33
35
|
import EventsScope from '../common/events/events-scope';
|
|
@@ -64,6 +66,7 @@ export default class Reachability extends EventsScope {
|
|
|
64
66
|
resultsCount = {videoMesh: {udp: 0}, public: {udp: 0, tcp: 0, xtls: 0}};
|
|
65
67
|
startTime = undefined;
|
|
66
68
|
totalDuration = undefined;
|
|
69
|
+
natType = NatType.Unknown;
|
|
67
70
|
|
|
68
71
|
protected lastTrigger?: string;
|
|
69
72
|
|
|
@@ -143,6 +146,10 @@ export default class Reachability extends EventsScope {
|
|
|
143
146
|
* @memberof Reachability
|
|
144
147
|
*/
|
|
145
148
|
public async gatherReachability(trigger: string): Promise<ReachabilityResults> {
|
|
149
|
+
// @ts-ignore
|
|
150
|
+
if (!this.webex.config.meetings.enableReachabilityChecks) {
|
|
151
|
+
throw new Error('enableReachabilityChecks is disabled in config');
|
|
152
|
+
}
|
|
146
153
|
// Fetch clusters and measure latency
|
|
147
154
|
try {
|
|
148
155
|
this.lastTrigger = trigger;
|
|
@@ -305,6 +312,7 @@ export default class Reachability extends EventsScope {
|
|
|
305
312
|
reachability_vmn_tcp_failed: 0,
|
|
306
313
|
reachability_vmn_xtls_success: 0,
|
|
307
314
|
reachability_vmn_xtls_failed: 0,
|
|
315
|
+
natType: this.natType,
|
|
308
316
|
};
|
|
309
317
|
|
|
310
318
|
const updateStats = (clusterType: 'public' | 'vmn', result: ClusterReachabilityResult) => {
|
|
@@ -963,6 +971,13 @@ export default class Reachability extends EventsScope {
|
|
|
963
971
|
}
|
|
964
972
|
);
|
|
965
973
|
|
|
974
|
+
this.clusterReachability[key].on(
|
|
975
|
+
Events.natTypeUpdated,
|
|
976
|
+
async (data: NatTypeUpdatedEventData) => {
|
|
977
|
+
this.natType = data.natType;
|
|
978
|
+
}
|
|
979
|
+
);
|
|
980
|
+
|
|
966
981
|
this.clusterReachability[key].start(); // not awaiting on purpose
|
|
967
982
|
});
|
|
968
983
|
}
|
|
@@ -7,6 +7,11 @@ export type TransportResult = {
|
|
|
7
7
|
clientMediaIPs?: string[];
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
export enum NatType {
|
|
11
|
+
Unknown = 'unknown',
|
|
12
|
+
SymmetricNat = 'symmetric-nat',
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
// reachability result for a specific media cluster
|
|
11
16
|
export type ClusterReachabilityResult = {
|
|
12
17
|
udp: TransportResult;
|
|
@@ -27,6 +32,7 @@ export type ReachabilityMetrics = {
|
|
|
27
32
|
reachability_vmn_tcp_failed: number;
|
|
28
33
|
reachability_vmn_xtls_success: number;
|
|
29
34
|
reachability_vmn_xtls_failed: number;
|
|
35
|
+
natType: NatType;
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
/**
|
|
@@ -45,6 +45,9 @@ class ReachabilityRequest {
|
|
|
45
45
|
joinCookie: any;
|
|
46
46
|
discoveryOptions?: Record<string, any>;
|
|
47
47
|
}> => {
|
|
48
|
+
const appType = this.webex?.config?.support?.appType;
|
|
49
|
+
const appVersion = this.webex?.config?.support?.appVersion;
|
|
50
|
+
|
|
48
51
|
// we only measure latency for the initial startup call, not for other triggers
|
|
49
52
|
const callWrapper =
|
|
50
53
|
trigger === 'startup'
|
|
@@ -67,6 +70,10 @@ class ReachabilityRequest {
|
|
|
67
70
|
'early-call-min-clusters': true,
|
|
68
71
|
},
|
|
69
72
|
'previous-report': previousReport,
|
|
73
|
+
...(appType &&
|
|
74
|
+
appVersion && {
|
|
75
|
+
'client-environment': {components: {[appType]: appVersion}},
|
|
76
|
+
}),
|
|
70
77
|
trigger,
|
|
71
78
|
},
|
|
72
79
|
timeout: this.webex.config.meetings.reachabilityGetClusterTimeout,
|
|
@@ -6,33 +6,37 @@ const canUserStart = (
|
|
|
6
6
|
displayHints: Array<string>,
|
|
7
7
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
8
8
|
): boolean =>
|
|
9
|
-
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START)
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START) &&
|
|
10
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies)) ||
|
|
11
|
+
(displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_START) &&
|
|
12
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_PREMISE_RECORD, userPolicies));
|
|
12
13
|
|
|
13
14
|
const canUserPause = (
|
|
14
15
|
displayHints: Array<string>,
|
|
15
16
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
16
17
|
): boolean =>
|
|
17
|
-
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE)
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE) &&
|
|
19
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies)) ||
|
|
20
|
+
(displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_PAUSE) &&
|
|
21
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_PREMISE_RECORD, userPolicies));
|
|
20
22
|
|
|
21
23
|
const canUserResume = (
|
|
22
24
|
displayHints: Array<string>,
|
|
23
25
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
24
26
|
): boolean =>
|
|
25
|
-
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME)
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME) &&
|
|
28
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies)) ||
|
|
29
|
+
(displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_RESUME) &&
|
|
30
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_PREMISE_RECORD, userPolicies));
|
|
28
31
|
|
|
29
32
|
const canUserStop = (
|
|
30
33
|
displayHints: Array<string>,
|
|
31
34
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
32
35
|
): boolean =>
|
|
33
|
-
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP) &&
|
|
37
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies)) ||
|
|
38
|
+
(displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_STOP) &&
|
|
39
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_PREMISE_RECORD, userPolicies));
|
|
36
40
|
|
|
37
41
|
const isPremiseRecordingEnabled = (
|
|
38
42
|
displayHints: Array<string>,
|
|
@@ -42,7 +46,7 @@ const isPremiseRecordingEnabled = (
|
|
|
42
46
|
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_PAUSE) ||
|
|
43
47
|
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_STOP) ||
|
|
44
48
|
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_RESUME)) &&
|
|
45
|
-
MeetingUtil.selfSupportsFeature(SELF_POLICY.
|
|
49
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_PREMISE_RECORD, userPolicies);
|
|
46
50
|
|
|
47
51
|
const extractLocusId = (url: string) => {
|
|
48
52
|
return url?.split('/').pop();
|
|
@@ -22,16 +22,54 @@ describe('plugin-meetings', () => {
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
describe('#initialize', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
interpretation.querySupportLanguages = sinon.stub();
|
|
27
|
+
interpretation.set({
|
|
28
|
+
canManageInterpreters: undefined,
|
|
29
|
+
hostSIEnabled: undefined,
|
|
30
|
+
locusUrl: undefined
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
interpretation.querySupportLanguages.reset();
|
|
36
|
+
});
|
|
37
|
+
|
|
25
38
|
it('creates SimultaneousInterpretation as expected', () => {
|
|
26
39
|
assert.equal(interpretation.namespace, 'Meetings');
|
|
27
40
|
});
|
|
28
41
|
it('call querySupportLanguages correctly when meet the conditions', () => {
|
|
29
|
-
interpretation.querySupportLanguages = sinon.stub();
|
|
30
42
|
interpretation.set({
|
|
31
43
|
canManageInterpreters: true,
|
|
44
|
+
hostSIEnabled: true,
|
|
45
|
+
locusUrl: "MOCK_LOCUS_URL"
|
|
32
46
|
});
|
|
33
47
|
assert.called(interpretation.querySupportLanguages);
|
|
34
48
|
});
|
|
49
|
+
|
|
50
|
+
it('does not call querySupportLanguages when canManageInterpreters is not set', () => {
|
|
51
|
+
interpretation.set({
|
|
52
|
+
hostSIEnabled: true,
|
|
53
|
+
locusUrl: "MOCK_LOCUS_URL"
|
|
54
|
+
});
|
|
55
|
+
assert.notCalled(interpretation.querySupportLanguages);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('does not call querySupportLanguages when hostSIEnabled is not set', () => {
|
|
59
|
+
interpretation.set({
|
|
60
|
+
canManageInterpreters: true,
|
|
61
|
+
locusUrl: "MOCK_LOCUS_URL"
|
|
62
|
+
});
|
|
63
|
+
assert.notCalled(interpretation.querySupportLanguages);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('does not call querySupportLanguages when locusUrl is not set', () => {
|
|
67
|
+
interpretation.set({
|
|
68
|
+
canManageInterpreters: true,
|
|
69
|
+
hostSIEnabled: true,
|
|
70
|
+
});
|
|
71
|
+
assert.notCalled(interpretation.querySupportLanguages);
|
|
72
|
+
});
|
|
35
73
|
});
|
|
36
74
|
|
|
37
75
|
describe('#cleanUp', () => {
|
|
@@ -269,6 +269,14 @@ describe('plugin-meetings', () => {
|
|
|
269
269
|
assert.equal(updates.hasPracticeSessionEnabledChanged, true);
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
+
it('returns hasPracticeSessionEnabledChanged = false when enabled is false and previous state is false', () => {
|
|
273
|
+
const newControls = {practiceSession: {enabled: false}};
|
|
274
|
+
|
|
275
|
+
const {updates} = ControlsUtils.getControls(defaultControls, newControls);
|
|
276
|
+
|
|
277
|
+
assert.equal(updates.hasPracticeSessionEnabledChanged, false);
|
|
278
|
+
});
|
|
279
|
+
|
|
272
280
|
it('returns hasEntryExitToneChanged = true when mode changed', () => {
|
|
273
281
|
const newControls = {
|
|
274
282
|
entryExitTone: {
|