@webex/plugin-meetings 3.8.0-next.14 → 3.8.0-next.16
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/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/index.js +13 -1
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +2 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/reachability/clusterReachability.js +52 -8
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +54 -35
- 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/types/meeting/index.d.ts +6 -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 +21 -21
- package/src/meeting/index.ts +13 -2
- package/src/meeting/util.ts +1 -1
- package/src/reachability/clusterReachability.ts +47 -1
- package/src/reachability/index.ts +11 -0
- package/src/reachability/reachability.types.ts +6 -0
- package/test/unit/spec/meeting/index.js +20 -4
- package/test/unit/spec/meeting/utils.js +44 -0
- package/test/unit/spec/reachability/clusterReachability.ts +47 -1
- package/test/unit/spec/reachability/index.ts +4 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ClusterNode } from './request';
|
|
2
2
|
import EventsScope from '../common/events/events-scope';
|
|
3
3
|
import { Enum } from '../constants';
|
|
4
|
-
import { ClusterReachabilityResult } from './reachability.types';
|
|
4
|
+
import { ClusterReachabilityResult, NatType } from './reachability.types';
|
|
5
5
|
export type ResultEventData = {
|
|
6
6
|
protocol: 'udp' | 'tcp' | 'xtls';
|
|
7
7
|
result: 'reachable' | 'unreachable' | 'untested';
|
|
@@ -12,9 +12,13 @@ export type ClientMediaIpsUpdatedEventData = {
|
|
|
12
12
|
protocol: 'udp' | 'tcp' | 'xtls';
|
|
13
13
|
clientMediaIPs: string[];
|
|
14
14
|
};
|
|
15
|
+
export type NatTypeUpdatedEventData = {
|
|
16
|
+
natType: NatType;
|
|
17
|
+
};
|
|
15
18
|
export declare const Events: {
|
|
16
19
|
readonly resultReady: "resultReady";
|
|
17
20
|
readonly clientMediaIpsUpdated: "clientMediaIpsUpdated";
|
|
21
|
+
readonly natTypeUpdated: "natTypeUpdated";
|
|
18
22
|
};
|
|
19
23
|
export type Events = Enum<typeof Events>;
|
|
20
24
|
/**
|
|
@@ -29,6 +33,7 @@ export declare class ClusterReachability extends EventsScope {
|
|
|
29
33
|
private pc?;
|
|
30
34
|
private defer;
|
|
31
35
|
private startTimestamp;
|
|
36
|
+
private srflxIceCandidates;
|
|
32
37
|
readonly isVideoMesh: boolean;
|
|
33
38
|
readonly name: any;
|
|
34
39
|
/**
|
|
@@ -107,6 +112,13 @@ export declare class ClusterReachability extends EventsScope {
|
|
|
107
112
|
* @returns {void}
|
|
108
113
|
*/
|
|
109
114
|
private saveResult;
|
|
115
|
+
/**
|
|
116
|
+
* Determines NAT Type.
|
|
117
|
+
*
|
|
118
|
+
* @param {RTCIceCandidate} candidate
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
private determineNatType;
|
|
110
122
|
/**
|
|
111
123
|
* Registers a listener for the icecandidate event
|
|
112
124
|
*
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Defer } from '@webex/common';
|
|
5
5
|
import { IP_VERSION } from '../constants';
|
|
6
6
|
import ReachabilityRequest, { ClusterList } from './request';
|
|
7
|
-
import { ClusterReachabilityResult, ClientMediaPreferences, ReachabilityMetrics, ReachabilityReportV0, ReachabilityReportV1, ReachabilityResults, ReachabilityResultsForBackend, GetClustersTrigger } from './reachability.types';
|
|
7
|
+
import { ClusterReachabilityResult, ClientMediaPreferences, ReachabilityMetrics, ReachabilityReportV0, ReachabilityReportV1, ReachabilityResults, ReachabilityResultsForBackend, GetClustersTrigger, NatType } from './reachability.types';
|
|
8
8
|
import { ClusterReachability } from './clusterReachability';
|
|
9
9
|
import EventsScope from '../common/events/events-scope';
|
|
10
10
|
/**
|
|
@@ -46,6 +46,7 @@ export default class Reachability extends EventsScope {
|
|
|
46
46
|
};
|
|
47
47
|
startTime: any;
|
|
48
48
|
totalDuration: any;
|
|
49
|
+
natType: NatType;
|
|
49
50
|
protected lastTrigger?: string;
|
|
50
51
|
/**
|
|
51
52
|
* Creates an instance of Reachability.
|
|
@@ -4,6 +4,10 @@ export type TransportResult = {
|
|
|
4
4
|
latencyInMilliseconds?: number;
|
|
5
5
|
clientMediaIPs?: string[];
|
|
6
6
|
};
|
|
7
|
+
export declare enum NatType {
|
|
8
|
+
Unknown = "unknown",
|
|
9
|
+
SymmetricNat = "symmetric-nat"
|
|
10
|
+
}
|
|
7
11
|
export type ClusterReachabilityResult = {
|
|
8
12
|
udp: TransportResult;
|
|
9
13
|
tcp: TransportResult;
|
|
@@ -22,6 +26,7 @@ export type ReachabilityMetrics = {
|
|
|
22
26
|
reachability_vmn_tcp_failed: number;
|
|
23
27
|
reachability_vmn_xtls_success: number;
|
|
24
28
|
reachability_vmn_xtls_failed: number;
|
|
29
|
+
natType: NatType;
|
|
25
30
|
};
|
|
26
31
|
/**
|
|
27
32
|
* This is the type that matches what backend expects us to send to them. It is a bit weird, because
|
package/dist/webinar/index.js
CHANGED
package/package.json
CHANGED
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
"@webex/eslint-config-legacy": "0.0.0",
|
|
44
44
|
"@webex/jest-config-legacy": "0.0.0",
|
|
45
45
|
"@webex/legacy-tools": "0.0.0",
|
|
46
|
-
"@webex/plugin-meetings": "3.8.0-next.
|
|
47
|
-
"@webex/plugin-rooms": "3.8.0-next.
|
|
48
|
-
"@webex/test-helper-chai": "3.8.0-next.
|
|
49
|
-
"@webex/test-helper-mocha": "3.8.0-next.
|
|
50
|
-
"@webex/test-helper-mock-webex": "3.8.0-next.
|
|
51
|
-
"@webex/test-helper-retry": "3.8.0-next.
|
|
52
|
-
"@webex/test-helper-test-users": "3.8.0-next.
|
|
46
|
+
"@webex/plugin-meetings": "3.8.0-next.16",
|
|
47
|
+
"@webex/plugin-rooms": "3.8.0-next.9",
|
|
48
|
+
"@webex/test-helper-chai": "3.8.0-next.8",
|
|
49
|
+
"@webex/test-helper-mocha": "3.8.0-next.8",
|
|
50
|
+
"@webex/test-helper-mock-webex": "3.8.0-next.8",
|
|
51
|
+
"@webex/test-helper-retry": "3.8.0-next.8",
|
|
52
|
+
"@webex/test-helper-test-users": "3.8.0-next.8",
|
|
53
53
|
"chai": "^4.3.4",
|
|
54
54
|
"chai-as-promised": "^7.1.1",
|
|
55
55
|
"eslint": "^8.24.0",
|
|
@@ -61,22 +61,22 @@
|
|
|
61
61
|
"typescript": "^4.7.4"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@webex/common": "3.8.0-next.
|
|
64
|
+
"@webex/common": "3.8.0-next.8",
|
|
65
65
|
"@webex/event-dictionary-ts": "^1.0.1688",
|
|
66
66
|
"@webex/internal-media-core": "2.14.4",
|
|
67
|
-
"@webex/internal-plugin-conversation": "3.8.0-next.
|
|
68
|
-
"@webex/internal-plugin-device": "3.8.0-next.
|
|
69
|
-
"@webex/internal-plugin-llm": "3.8.0-next.
|
|
70
|
-
"@webex/internal-plugin-mercury": "3.8.0-next.
|
|
71
|
-
"@webex/internal-plugin-metrics": "3.8.0-next.
|
|
72
|
-
"@webex/internal-plugin-support": "3.8.0-next.
|
|
73
|
-
"@webex/internal-plugin-user": "3.8.0-next.
|
|
74
|
-
"@webex/internal-plugin-voicea": "3.8.0-next.
|
|
75
|
-
"@webex/media-helpers": "3.8.0-next.
|
|
76
|
-
"@webex/plugin-people": "3.8.0-next.
|
|
77
|
-
"@webex/plugin-rooms": "3.8.0-next.
|
|
67
|
+
"@webex/internal-plugin-conversation": "3.8.0-next.9",
|
|
68
|
+
"@webex/internal-plugin-device": "3.8.0-next.8",
|
|
69
|
+
"@webex/internal-plugin-llm": "3.8.0-next.10",
|
|
70
|
+
"@webex/internal-plugin-mercury": "3.8.0-next.9",
|
|
71
|
+
"@webex/internal-plugin-metrics": "3.8.0-next.8",
|
|
72
|
+
"@webex/internal-plugin-support": "3.8.0-next.9",
|
|
73
|
+
"@webex/internal-plugin-user": "3.8.0-next.8",
|
|
74
|
+
"@webex/internal-plugin-voicea": "3.8.0-next.16",
|
|
75
|
+
"@webex/media-helpers": "3.8.0-next.8",
|
|
76
|
+
"@webex/plugin-people": "3.8.0-next.9",
|
|
77
|
+
"@webex/plugin-rooms": "3.8.0-next.9",
|
|
78
78
|
"@webex/web-capabilities": "^1.4.0",
|
|
79
|
-
"@webex/webex-core": "3.8.0-next.
|
|
79
|
+
"@webex/webex-core": "3.8.0-next.8",
|
|
80
80
|
"ampersand-collection": "^2.0.2",
|
|
81
81
|
"bowser": "^2.11.0",
|
|
82
82
|
"btoa": "^1.2.1",
|
|
@@ -92,5 +92,5 @@
|
|
|
92
92
|
"//": [
|
|
93
93
|
"TODO: upgrade jwt-decode when moving to node 18"
|
|
94
94
|
],
|
|
95
|
-
"version": "3.8.0-next.
|
|
95
|
+
"version": "3.8.0-next.16"
|
|
96
96
|
}
|
package/src/meeting/index.ts
CHANGED
|
@@ -1686,6 +1686,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1686
1686
|
return this.#isoLocalClientMeetingJoinTime;
|
|
1687
1687
|
}
|
|
1688
1688
|
|
|
1689
|
+
/**
|
|
1690
|
+
* Setter - sets isoLocalClientMeetingJoinTime
|
|
1691
|
+
* This will be set once on meeting join, and not updated again
|
|
1692
|
+
* @param {string | undefined} time in ISO format
|
|
1693
|
+
*/
|
|
1694
|
+
set isoLocalClientMeetingJoinTime(time: string | undefined) {
|
|
1695
|
+
if (!time) {
|
|
1696
|
+
this.#isoLocalClientMeetingJoinTime = new Date().toISOString();
|
|
1697
|
+
} else {
|
|
1698
|
+
this.#isoLocalClientMeetingJoinTime = time;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1689
1702
|
/**
|
|
1690
1703
|
* Set meeting info and trigger `MEETING_INFO_AVAILABLE` event
|
|
1691
1704
|
* @param {any} info
|
|
@@ -5718,8 +5731,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5718
5731
|
// @ts-ignore
|
|
5719
5732
|
this.webex.internal.device.meetingStarted();
|
|
5720
5733
|
|
|
5721
|
-
this.#isoLocalClientMeetingJoinTime = new Date().toISOString();
|
|
5722
|
-
|
|
5723
5734
|
LoggerProxy.logger.log('Meeting:index#join --> Success');
|
|
5724
5735
|
|
|
5725
5736
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.JOIN_SUCCESS, {
|
package/src/meeting/util.ts
CHANGED
|
@@ -180,7 +180,7 @@ const MeetingUtil = {
|
|
|
180
180
|
.then((res) => {
|
|
181
181
|
const parsed = MeetingUtil.parseLocusJoin(res);
|
|
182
182
|
meeting.setLocus(parsed);
|
|
183
|
-
|
|
183
|
+
meeting.isoLocalClientMeetingJoinTime = res?.headers?.date; // read from header if exist, else fall back to system clock : https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-555657
|
|
184
184
|
webex.internal.newMetrics.submitClientEvent({
|
|
185
185
|
name: 'client.locus.join.response',
|
|
186
186
|
payload: {
|
|
@@ -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
|
|
|
@@ -309,6 +312,7 @@ export default class Reachability extends EventsScope {
|
|
|
309
312
|
reachability_vmn_tcp_failed: 0,
|
|
310
313
|
reachability_vmn_xtls_success: 0,
|
|
311
314
|
reachability_vmn_xtls_failed: 0,
|
|
315
|
+
natType: this.natType,
|
|
312
316
|
};
|
|
313
317
|
|
|
314
318
|
const updateStats = (clusterType: 'public' | 'vmn', result: ClusterReachabilityResult) => {
|
|
@@ -967,6 +971,13 @@ export default class Reachability extends EventsScope {
|
|
|
967
971
|
}
|
|
968
972
|
);
|
|
969
973
|
|
|
974
|
+
this.clusterReachability[key].on(
|
|
975
|
+
Events.natTypeUpdated,
|
|
976
|
+
async (data: NatTypeUpdatedEventData) => {
|
|
977
|
+
this.natType = data.natType;
|
|
978
|
+
}
|
|
979
|
+
);
|
|
980
|
+
|
|
970
981
|
this.clusterReachability[key].start(); // not awaiting on purpose
|
|
971
982
|
});
|
|
972
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
|
/**
|
|
@@ -210,6 +210,7 @@ describe('plugin-meetings', () => {
|
|
|
210
210
|
let membersSpy;
|
|
211
211
|
let meetingRequestSpy;
|
|
212
212
|
let correlationId;
|
|
213
|
+
let isoLocalClientMeetingJoinTime;
|
|
213
214
|
let uploadEvent;
|
|
214
215
|
|
|
215
216
|
beforeEach(() => {
|
|
@@ -1692,10 +1693,6 @@ describe('plugin-meetings', () => {
|
|
|
1692
1693
|
sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
|
|
1693
1694
|
});
|
|
1694
1695
|
|
|
1695
|
-
afterEach(() => {
|
|
1696
|
-
assert.exists(meeting.isoLocalClientMeetingJoinTime);
|
|
1697
|
-
});
|
|
1698
|
-
|
|
1699
1696
|
it('should join the meeting and return promise', async () => {
|
|
1700
1697
|
const join = meeting.join({pstnAudioType: 'dial-in'});
|
|
1701
1698
|
meeting.config.enableAutomaticLLM = true;
|
|
@@ -7530,6 +7527,25 @@ describe('plugin-meetings', () => {
|
|
|
7530
7527
|
});
|
|
7531
7528
|
});
|
|
7532
7529
|
|
|
7530
|
+
describe('#setIsoLocalClientMeetingJoinTime', () => {
|
|
7531
|
+
it('should set the isoLocalClientMeetingJoinTime when passed in', () => {
|
|
7532
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, isoLocalClientMeetingJoinTime);
|
|
7533
|
+
meeting.isoLocalClientMeetingJoinTime = 'test';
|
|
7534
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, 'test');
|
|
7535
|
+
meeting.isoLocalClientMeetingJoinTime = 'test2';
|
|
7536
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, 'test2');
|
|
7537
|
+
});
|
|
7538
|
+
|
|
7539
|
+
it('should set the isoLocalClientMeetingJoin time once and only once when not passed in', () => {
|
|
7540
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, isoLocalClientMeetingJoinTime);
|
|
7541
|
+
meeting.isoLocalClientMeetingJoinTime = undefined;
|
|
7542
|
+
const time = meeting.isoLocalClientMeetingJoinTime;
|
|
7543
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, time);
|
|
7544
|
+
meeting.isoLocalClientMeetingJoinTime = 'test2';
|
|
7545
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, 'test2');
|
|
7546
|
+
});
|
|
7547
|
+
});
|
|
7548
|
+
|
|
7533
7549
|
describe('#updateCallStateForMetrics', () => {
|
|
7534
7550
|
it('should update the callState, overriding existing values', () => {
|
|
7535
7551
|
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
|
@@ -460,6 +460,50 @@ describe('plugin-meetings', () => {
|
|
|
460
460
|
});
|
|
461
461
|
});
|
|
462
462
|
|
|
463
|
+
|
|
464
|
+
it('#Should call `meetingRequest.joinMeeting and handle a date header in the response : isoLocalClientMeetingJoinedTime', async () => {
|
|
465
|
+
meeting.isMultistream = true;
|
|
466
|
+
|
|
467
|
+
const FAKE_REACHABILITY_REPORT = {
|
|
468
|
+
id: 'fake reachability report',
|
|
469
|
+
};
|
|
470
|
+
const FAKE_CLIENT_MEDIA_PREFERENCES = {
|
|
471
|
+
id: 'fake client media preferences',
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
webex.meetings.reachability.getReachabilityReportToAttachToRoap.resolves(FAKE_REACHABILITY_REPORT);
|
|
475
|
+
webex.meetings.reachability.getClientMediaPreferences.resolves(FAKE_CLIENT_MEDIA_PREFERENCES);
|
|
476
|
+
|
|
477
|
+
sinon
|
|
478
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4')
|
|
479
|
+
.get(() => true);
|
|
480
|
+
sinon
|
|
481
|
+
.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6')
|
|
482
|
+
.get(() => true);
|
|
483
|
+
|
|
484
|
+
meeting.meetingRequest.joinMeeting.resolves({
|
|
485
|
+
headers: {
|
|
486
|
+
date: 'test'
|
|
487
|
+
},
|
|
488
|
+
body: {
|
|
489
|
+
mediaConnections: [{mediaId: 'test'}],
|
|
490
|
+
locus: {
|
|
491
|
+
url: 'test',
|
|
492
|
+
self: {
|
|
493
|
+
id: 'test'
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
await MeetingUtil.joinMeeting(meeting, {
|
|
500
|
+
reachability: 'reachability',
|
|
501
|
+
roapMessage: 'roapMessage',
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, 'test');
|
|
505
|
+
});
|
|
506
|
+
|
|
463
507
|
it('should handle failed reachability report retrieval', async () => {
|
|
464
508
|
webex.meetings.reachability.getReachabilityReportToAttachToRoap.rejects(
|
|
465
509
|
new Error('fake error')
|
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
ResultEventData,
|
|
10
10
|
Events,
|
|
11
11
|
ClientMediaIpsUpdatedEventData,
|
|
12
|
+
NatTypeUpdatedEventData,
|
|
12
13
|
} from '@webex/plugin-meetings/src/reachability/clusterReachability'; // replace with actual path
|
|
14
|
+
import { NatType } from 'packages/@webex/plugin-meetings/dist/reachability/reachability.types';
|
|
13
15
|
|
|
14
16
|
describe('ClusterReachability', () => {
|
|
15
17
|
let previousRTCPeerConnection;
|
|
@@ -17,15 +19,17 @@ describe('ClusterReachability', () => {
|
|
|
17
19
|
let fakePeerConnection;
|
|
18
20
|
let gatherIceCandidatesSpy;
|
|
19
21
|
|
|
20
|
-
const emittedEvents: Record<Events, (ResultEventData | ClientMediaIpsUpdatedEventData)[]> = {
|
|
22
|
+
const emittedEvents: Record<Events, (ResultEventData | ClientMediaIpsUpdatedEventData | NatTypeUpdatedEventData)[]> = {
|
|
21
23
|
[Events.resultReady]: [],
|
|
22
24
|
[Events.clientMediaIpsUpdated]: [],
|
|
25
|
+
[Events.natTypeUpdated]: [],
|
|
23
26
|
};
|
|
24
27
|
const FAKE_OFFER = {type: 'offer', sdp: 'fake sdp'};
|
|
25
28
|
|
|
26
29
|
const resetEmittedEvents = () => {
|
|
27
30
|
emittedEvents[Events.resultReady].length = 0;
|
|
28
31
|
emittedEvents[Events.clientMediaIpsUpdated].length = 0;
|
|
32
|
+
emittedEvents[Events.natTypeUpdated].length = 0;
|
|
29
33
|
};
|
|
30
34
|
beforeEach(() => {
|
|
31
35
|
fakePeerConnection = {
|
|
@@ -56,6 +60,10 @@ describe('ClusterReachability', () => {
|
|
|
56
60
|
clusterReachability.on(Events.clientMediaIpsUpdated, (data: ClientMediaIpsUpdatedEventData) => {
|
|
57
61
|
emittedEvents[Events.clientMediaIpsUpdated].push(data);
|
|
58
62
|
});
|
|
63
|
+
|
|
64
|
+
clusterReachability.on(Events.natTypeUpdated, (data: NatTypeUpdatedEventData) => {
|
|
65
|
+
emittedEvents[Events.natTypeUpdated].push(data);
|
|
66
|
+
});
|
|
59
67
|
});
|
|
60
68
|
|
|
61
69
|
afterEach(() => {
|
|
@@ -440,5 +448,43 @@ describe('ClusterReachability', () => {
|
|
|
440
448
|
xtls: {result: 'reachable', latencyInMilliseconds: 40},
|
|
441
449
|
});
|
|
442
450
|
});
|
|
451
|
+
|
|
452
|
+
it('determines correctly if symmetric-nat is detected', async () => {
|
|
453
|
+
const promise = clusterReachability.start();
|
|
454
|
+
|
|
455
|
+
// generate candidates with duplicate addresses
|
|
456
|
+
await clock.tickAsync(10);
|
|
457
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1', relatedPort: 3478, port: 1000}});
|
|
458
|
+
|
|
459
|
+
// check events emitted: there shouldn't be any natTypeUpdated emitted
|
|
460
|
+
assert.equal(emittedEvents[Events.natTypeUpdated].length, 0);
|
|
461
|
+
|
|
462
|
+
await clock.tickAsync(10);
|
|
463
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1', relatedPort: 3478, port: 2000}});
|
|
464
|
+
|
|
465
|
+
// should emit natTypeUpdated event
|
|
466
|
+
assert.equal(emittedEvents[Events.natTypeUpdated].length, 1);
|
|
467
|
+
assert.deepEqual(emittedEvents[Events.natTypeUpdated][0], {
|
|
468
|
+
natType: 'symmetric-nat',
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// send also a relay candidate so that the reachability check finishes
|
|
472
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp'}});
|
|
473
|
+
fakePeerConnection.onicecandidate({
|
|
474
|
+
candidate: {type: 'relay', address: 'someTurnRelayIp', port: 443},
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
await promise;
|
|
478
|
+
|
|
479
|
+
assert.deepEqual(clusterReachability.getResult(), {
|
|
480
|
+
udp: {
|
|
481
|
+
result: 'reachable',
|
|
482
|
+
latencyInMilliseconds: 10,
|
|
483
|
+
clientMediaIPs: ['somePublicIp1'],
|
|
484
|
+
},
|
|
485
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 20},
|
|
486
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 20},
|
|
487
|
+
});
|
|
488
|
+
});
|
|
443
489
|
});
|
|
444
490
|
});
|
|
@@ -2156,6 +2156,7 @@ describe('getReachabilityMetrics', () => {
|
|
|
2156
2156
|
reachability_vmn_tcp_failed: 0,
|
|
2157
2157
|
reachability_vmn_xtls_success: 0,
|
|
2158
2158
|
reachability_vmn_xtls_failed: 0,
|
|
2159
|
+
natType: 'unknown'
|
|
2159
2160
|
});
|
|
2160
2161
|
});
|
|
2161
2162
|
|
|
@@ -2223,6 +2224,7 @@ describe('getReachabilityMetrics', () => {
|
|
|
2223
2224
|
reachability_vmn_tcp_failed: 1,
|
|
2224
2225
|
reachability_vmn_xtls_success: 0,
|
|
2225
2226
|
reachability_vmn_xtls_failed: 0,
|
|
2227
|
+
natType: 'unknown'
|
|
2226
2228
|
}
|
|
2227
2229
|
);
|
|
2228
2230
|
});
|
|
@@ -2284,6 +2286,7 @@ describe('getReachabilityMetrics', () => {
|
|
|
2284
2286
|
reachability_vmn_tcp_failed: 0,
|
|
2285
2287
|
reachability_vmn_xtls_success: 0,
|
|
2286
2288
|
reachability_vmn_xtls_failed: 0,
|
|
2289
|
+
natType: 'unknown'
|
|
2287
2290
|
}
|
|
2288
2291
|
);
|
|
2289
2292
|
});
|
|
@@ -2345,6 +2348,7 @@ describe('getReachabilityMetrics', () => {
|
|
|
2345
2348
|
reachability_vmn_tcp_failed: 3,
|
|
2346
2349
|
reachability_vmn_xtls_success: 1,
|
|
2347
2350
|
reachability_vmn_xtls_failed: 1,
|
|
2351
|
+
natType: 'unknown'
|
|
2348
2352
|
}
|
|
2349
2353
|
);
|
|
2350
2354
|
});
|