@webex/plugin-meetings 3.8.0-next.55 → 3.8.0-next.56
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 +72 -34
- package/dist/meeting/index.js.map +1 -1
- package/dist/types/meeting/index.d.ts +6 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +3 -3
- package/src/meeting/index.ts +47 -15
- package/test/unit/spec/meeting/index.js +124 -10
@@ -1976,5 +1976,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
1976
1976
|
* @returns {Promise<void>}
|
1977
1977
|
*/
|
1978
1978
|
checkAndRefreshPermissionToken(threshold: number, reason: string): Promise<void>;
|
1979
|
+
/**
|
1980
|
+
* Gets the media reachability metrics
|
1981
|
+
*
|
1982
|
+
* @returns {Promise<MediaReachabilityMetrics>}
|
1983
|
+
*/
|
1984
|
+
private getMediaReachabilityMetricFields;
|
1979
1985
|
}
|
1980
1986
|
export {};
|
package/dist/webinar/index.js
CHANGED
package/package.json
CHANGED
@@ -43,7 +43,7 @@
|
|
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.
|
46
|
+
"@webex/plugin-meetings": "3.8.0-next.56",
|
47
47
|
"@webex/plugin-rooms": "3.8.0-next.21",
|
48
48
|
"@webex/test-helper-chai": "3.8.0-next.17",
|
49
49
|
"@webex/test-helper-mocha": "3.8.0-next.17",
|
@@ -71,7 +71,7 @@
|
|
71
71
|
"@webex/internal-plugin-metrics": "3.8.0-next.17",
|
72
72
|
"@webex/internal-plugin-support": "3.8.0-next.21",
|
73
73
|
"@webex/internal-plugin-user": "3.8.0-next.17",
|
74
|
-
"@webex/internal-plugin-voicea": "3.8.0-next.
|
74
|
+
"@webex/internal-plugin-voicea": "3.8.0-next.56",
|
75
75
|
"@webex/media-helpers": "3.8.0-next.19",
|
76
76
|
"@webex/plugin-people": "3.8.0-next.19",
|
77
77
|
"@webex/plugin-rooms": "3.8.0-next.21",
|
@@ -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.56"
|
96
96
|
}
|
package/src/meeting/index.ts
CHANGED
@@ -164,6 +164,7 @@ import Member from '../member';
|
|
164
164
|
import {BrbState, createBrbState} from './brbState';
|
165
165
|
import MultistreamNotSupportedError from '../common/errors/multistream-not-supported-error';
|
166
166
|
import JoinForbiddenError from '../common/errors/join-forbidden-error';
|
167
|
+
import {ReachabilityMetrics} from '../reachability/reachability.types';
|
167
168
|
|
168
169
|
// default callback so we don't call an undefined function, but in practice it should never be used
|
169
170
|
const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
|
@@ -262,6 +263,8 @@ type FetchMeetingInfoParams = {
|
|
262
263
|
sendCAevents?: boolean;
|
263
264
|
};
|
264
265
|
|
266
|
+
type MediaReachabilityMetrics = ReachabilityMetrics & {isSubnetReachable: boolean};
|
267
|
+
|
265
268
|
/**
|
266
269
|
* MediaDirection
|
267
270
|
* @typedef {Object} MediaDirection
|
@@ -7769,14 +7772,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7769
7772
|
|
7770
7773
|
const {connectionType, selectedCandidatePairChanges, numTransports} =
|
7771
7774
|
await this.mediaProperties.getCurrentConnectionInfo();
|
7772
|
-
|
7773
|
-
const reachabilityStats = await this.webex.meetings.reachability.getReachabilityMetrics();
|
7775
|
+
|
7774
7776
|
const iceCandidateErrors = Object.fromEntries(this.iceCandidateErrors);
|
7775
7777
|
|
7776
|
-
|
7777
|
-
const isSubnetReachable = this.webex.meetings.reachability.isSubnetReachable(
|
7778
|
-
this.mediaServerIp
|
7779
|
-
);
|
7778
|
+
const reachabilityMetrics = await this.getMediaReachabilityMetricFields();
|
7780
7779
|
|
7781
7780
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS, {
|
7782
7781
|
correlation_id: this.correlationId,
|
@@ -7787,8 +7786,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7787
7786
|
isMultistream: this.isMultistream,
|
7788
7787
|
retriedWithTurnServer: this.addMediaData.retriedWithTurnServer,
|
7789
7788
|
isJoinWithMediaRetry: this.joinWithMediaRetryInfo.isRetry,
|
7790
|
-
|
7791
|
-
...reachabilityStats,
|
7789
|
+
...reachabilityMetrics,
|
7792
7790
|
...iceCandidateErrors,
|
7793
7791
|
iceCandidatesCount: this.iceCandidatesCount,
|
7794
7792
|
});
|
@@ -7810,18 +7808,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7810
7808
|
LoggerProxy.logger.error(`${LOG_HEADER} failed to establish media connection: `, error);
|
7811
7809
|
|
7812
7810
|
// @ts-ignore
|
7813
|
-
const reachabilityMetrics = await this.
|
7811
|
+
const reachabilityMetrics = await this.getMediaReachabilityMetricFields();
|
7814
7812
|
|
7815
7813
|
const {selectedCandidatePairChanges, numTransports} =
|
7816
7814
|
await this.mediaProperties.getCurrentConnectionInfo();
|
7817
7815
|
|
7818
7816
|
const iceCandidateErrors = Object.fromEntries(this.iceCandidateErrors);
|
7819
7817
|
|
7820
|
-
// @ts-ignore
|
7821
|
-
const isSubnetReachable = this.webex.meetings.reachability.isSubnetReachable(
|
7822
|
-
this.mediaServerIp
|
7823
|
-
);
|
7824
|
-
|
7825
7818
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
|
7826
7819
|
correlation_id: this.correlationId,
|
7827
7820
|
locus_id: this.locusUrl.split('/').pop(),
|
@@ -7851,7 +7844,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7851
7844
|
this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc?.iceConnectionState ||
|
7852
7845
|
'unknown',
|
7853
7846
|
...reachabilityMetrics,
|
7854
|
-
isSubnetReachable,
|
7855
7847
|
...iceCandidateErrors,
|
7856
7848
|
iceCandidatesCount: this.iceCandidatesCount,
|
7857
7849
|
});
|
@@ -9622,4 +9614,44 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
9622
9614
|
|
9623
9615
|
return Promise.resolve();
|
9624
9616
|
}
|
9617
|
+
|
9618
|
+
/**
|
9619
|
+
* Gets the media reachability metrics
|
9620
|
+
*
|
9621
|
+
* @returns {Promise<MediaReachabilityMetrics>}
|
9622
|
+
*/
|
9623
|
+
private async getMediaReachabilityMetricFields(): Promise<MediaReachabilityMetrics> {
|
9624
|
+
const reachabilityMetrics: ReachabilityMetrics =
|
9625
|
+
// @ts-ignore
|
9626
|
+
await this.webex.meetings.reachability.getReachabilityMetrics();
|
9627
|
+
|
9628
|
+
const successKeys: Array<keyof ReachabilityMetrics> = [
|
9629
|
+
'reachability_public_udp_success',
|
9630
|
+
'reachability_public_tcp_success',
|
9631
|
+
'reachability_public_xtls_success',
|
9632
|
+
'reachability_vmn_udp_success',
|
9633
|
+
'reachability_vmn_tcp_success',
|
9634
|
+
'reachability_vmn_xtls_success',
|
9635
|
+
];
|
9636
|
+
|
9637
|
+
const totalSuccessCases = successKeys.reduce((total, key) => {
|
9638
|
+
const value = reachabilityMetrics[key];
|
9639
|
+
if (typeof value === 'number') {
|
9640
|
+
return total + value;
|
9641
|
+
}
|
9642
|
+
|
9643
|
+
return total;
|
9644
|
+
}, 0);
|
9645
|
+
|
9646
|
+
let isSubnetReachable = null;
|
9647
|
+
if (totalSuccessCases > 0) {
|
9648
|
+
// @ts-ignore
|
9649
|
+
isSubnetReachable = this.webex.meetings.reachability.isSubnetReachable(this.mediaServerIp);
|
9650
|
+
}
|
9651
|
+
|
9652
|
+
return {
|
9653
|
+
...reachabilityMetrics,
|
9654
|
+
isSubnetReachable,
|
9655
|
+
};
|
9656
|
+
}
|
9625
9657
|
}
|
@@ -2166,7 +2166,7 @@ describe('plugin-meetings', () => {
|
|
2166
2166
|
someReachabilityMetric1: 'some value1',
|
2167
2167
|
someReachabilityMetric2: 'some value2',
|
2168
2168
|
selectedCandidatePairChanges: 2,
|
2169
|
-
isSubnetReachable:
|
2169
|
+
isSubnetReachable: null,
|
2170
2170
|
numTransports: 1,
|
2171
2171
|
iceCandidatesCount: 0,
|
2172
2172
|
}
|
@@ -2213,7 +2213,7 @@ describe('plugin-meetings', () => {
|
|
2213
2213
|
signalingState: 'unknown',
|
2214
2214
|
connectionState: 'unknown',
|
2215
2215
|
iceConnectionState: 'unknown',
|
2216
|
-
isSubnetReachable:
|
2216
|
+
isSubnetReachable: null,
|
2217
2217
|
})
|
2218
2218
|
);
|
2219
2219
|
|
@@ -2279,7 +2279,7 @@ describe('plugin-meetings', () => {
|
|
2279
2279
|
selectedCandidatePairChanges: 2,
|
2280
2280
|
numTransports: 1,
|
2281
2281
|
iceCandidatesCount: 0,
|
2282
|
-
isSubnetReachable:
|
2282
|
+
isSubnetReachable: null,
|
2283
2283
|
}
|
2284
2284
|
);
|
2285
2285
|
});
|
@@ -2337,7 +2337,7 @@ describe('plugin-meetings', () => {
|
|
2337
2337
|
signalingState: 'have-local-offer',
|
2338
2338
|
connectionState: 'connecting',
|
2339
2339
|
iceConnectionState: 'checking',
|
2340
|
-
isSubnetReachable:
|
2340
|
+
isSubnetReachable: null,
|
2341
2341
|
})
|
2342
2342
|
);
|
2343
2343
|
|
@@ -2395,7 +2395,7 @@ describe('plugin-meetings', () => {
|
|
2395
2395
|
signalingState: 'have-local-offer',
|
2396
2396
|
connectionState: 'connecting',
|
2397
2397
|
iceConnectionState: 'checking',
|
2398
|
-
isSubnetReachable:
|
2398
|
+
isSubnetReachable: null,
|
2399
2399
|
})
|
2400
2400
|
);
|
2401
2401
|
|
@@ -2731,7 +2731,7 @@ describe('plugin-meetings', () => {
|
|
2731
2731
|
sinon.stub().returns(FAKE_ERROR));
|
2732
2732
|
webex.meetings.reachability = {
|
2733
2733
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(false),
|
2734
|
-
getReachabilityMetrics: sinon.stub().resolves(),
|
2734
|
+
getReachabilityMetrics: sinon.stub().resolves({}),
|
2735
2735
|
stopReachability: sinon.stub(),
|
2736
2736
|
isSubnetReachable: sinon.stub().returns(true),
|
2737
2737
|
};
|
@@ -2917,7 +2917,7 @@ describe('plugin-meetings', () => {
|
|
2917
2917
|
selectedCandidatePairChanges: 2,
|
2918
2918
|
numTransports: 1,
|
2919
2919
|
iceCandidatesCount: 0,
|
2920
|
-
isSubnetReachable:
|
2920
|
+
isSubnetReachable: null,
|
2921
2921
|
},
|
2922
2922
|
]);
|
2923
2923
|
|
@@ -3120,7 +3120,7 @@ describe('plugin-meetings', () => {
|
|
3120
3120
|
retriedWithTurnServer: true,
|
3121
3121
|
isJoinWithMediaRetry: false,
|
3122
3122
|
iceCandidatesCount: 0,
|
3123
|
-
isSubnetReachable:
|
3123
|
+
isSubnetReachable: null,
|
3124
3124
|
},
|
3125
3125
|
]);
|
3126
3126
|
meeting.roap.doTurnDiscovery;
|
@@ -3277,7 +3277,7 @@ describe('plugin-meetings', () => {
|
|
3277
3277
|
iceCandidatesCount: 3,
|
3278
3278
|
'701_error': 3,
|
3279
3279
|
'701_turn_host_lookup_received_error': 1,
|
3280
|
-
isSubnetReachable:
|
3280
|
+
isSubnetReachable: null,
|
3281
3281
|
}
|
3282
3282
|
);
|
3283
3283
|
|
@@ -3340,7 +3340,7 @@ describe('plugin-meetings', () => {
|
|
3340
3340
|
iceConnectionState: 'unknown',
|
3341
3341
|
selectedCandidatePairChanges: 2,
|
3342
3342
|
numTransports: 1,
|
3343
|
-
isSubnetReachable:
|
3343
|
+
isSubnetReachable: null,
|
3344
3344
|
iceCandidatesCount: 0,
|
3345
3345
|
}
|
3346
3346
|
);
|
@@ -3402,6 +3402,120 @@ describe('plugin-meetings', () => {
|
|
3402
3402
|
numTransports: 1,
|
3403
3403
|
'701_error': 2,
|
3404
3404
|
'701_turn_host_lookup_received_error': 1,
|
3405
|
+
isSubnetReachable: null,
|
3406
|
+
iceCandidatesCount: 0,
|
3407
|
+
}
|
3408
|
+
);
|
3409
|
+
|
3410
|
+
assert.isOk(errorThrown);
|
3411
|
+
});
|
3412
|
+
|
3413
|
+
it('should send valid isSubnetReachability if media connection success', async () => {
|
3414
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3415
|
+
turnServerInfo: undefined,
|
3416
|
+
turnDiscoverySkippedReason: undefined,
|
3417
|
+
});
|
3418
|
+
meeting.meetingState = 'ACTIVE';
|
3419
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.resolves();
|
3420
|
+
meeting.webex.meetings.reachability = {
|
3421
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3422
|
+
reachability_public_udp_success: 5,
|
3423
|
+
}),
|
3424
|
+
stopReachability: sinon.stub(),
|
3425
|
+
isSubnetReachable: sinon.stub().returns(false),
|
3426
|
+
};
|
3427
|
+
|
3428
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3429
|
+
const closeMediaConnectionStub = sinon.stub();
|
3430
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3431
|
+
close: closeMediaConnectionStub,
|
3432
|
+
forceRtcMetricsSend,
|
3433
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3434
|
+
initiateOffer: sinon.stub().resolves({}),
|
3435
|
+
on: sinon.stub(),
|
3436
|
+
});
|
3437
|
+
|
3438
|
+
await meeting
|
3439
|
+
.addMedia({
|
3440
|
+
mediaSettings: {},
|
3441
|
+
});
|
3442
|
+
|
3443
|
+
assert.calledWith(
|
3444
|
+
Metrics.sendBehavioralMetric,
|
3445
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS,
|
3446
|
+
{
|
3447
|
+
correlation_id: meeting.correlationId,
|
3448
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3449
|
+
connectionType: 'udp',
|
3450
|
+
selectedCandidatePairChanges: 2,
|
3451
|
+
numTransports: 1,
|
3452
|
+
isMultistream: false,
|
3453
|
+
retriedWithTurnServer: false,
|
3454
|
+
isJoinWithMediaRetry: false,
|
3455
|
+
iceCandidatesCount: 0,
|
3456
|
+
reachability_public_udp_success: 5,
|
3457
|
+
isSubnetReachable: false,
|
3458
|
+
}
|
3459
|
+
);
|
3460
|
+
});
|
3461
|
+
|
3462
|
+
it('should send valid isSubnetReachability if media connection fails', async () => {
|
3463
|
+
let errorThrown = undefined;
|
3464
|
+
|
3465
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3466
|
+
turnServerInfo: undefined,
|
3467
|
+
turnDiscoverySkippedReason: undefined,
|
3468
|
+
});
|
3469
|
+
meeting.meetingState = 'ACTIVE';
|
3470
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects({iceConnected: false});
|
3471
|
+
meeting.webex.meetings.reachability = {
|
3472
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3473
|
+
reachability_public_udp_success: 5,
|
3474
|
+
}),
|
3475
|
+
stopReachability: sinon.stub(),
|
3476
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3477
|
+
};
|
3478
|
+
|
3479
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3480
|
+
const closeMediaConnectionStub = sinon.stub();
|
3481
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3482
|
+
close: closeMediaConnectionStub,
|
3483
|
+
forceRtcMetricsSend,
|
3484
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3485
|
+
initiateOffer: sinon.stub().resolves({}),
|
3486
|
+
on: sinon.stub(),
|
3487
|
+
});
|
3488
|
+
|
3489
|
+
await meeting
|
3490
|
+
.addMedia({
|
3491
|
+
mediaSettings: {},
|
3492
|
+
})
|
3493
|
+
.catch((err) => {
|
3494
|
+
errorThrown = err;
|
3495
|
+
assert.instanceOf(err, AddMediaFailed);
|
3496
|
+
});
|
3497
|
+
|
3498
|
+
// Check that the only metric sent is ADD_MEDIA_FAILURE
|
3499
|
+
assert.calledOnceWithExactly(
|
3500
|
+
Metrics.sendBehavioralMetric,
|
3501
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
|
3502
|
+
{
|
3503
|
+
correlation_id: meeting.correlationId,
|
3504
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3505
|
+
reason: errorThrown.message,
|
3506
|
+
stack: errorThrown.stack,
|
3507
|
+
code: errorThrown.code,
|
3508
|
+
turnDiscoverySkippedReason: undefined,
|
3509
|
+
turnServerUsed: true,
|
3510
|
+
retriedWithTurnServer: false,
|
3511
|
+
isMultistream: false,
|
3512
|
+
isJoinWithMediaRetry: false,
|
3513
|
+
signalingState: 'unknown',
|
3514
|
+
connectionState: 'unknown',
|
3515
|
+
iceConnectionState: 'unknown',
|
3516
|
+
selectedCandidatePairChanges: 2,
|
3517
|
+
numTransports: 1,
|
3518
|
+
reachability_public_udp_success: 5,
|
3405
3519
|
isSubnetReachable: true,
|
3406
3520
|
iceCandidatesCount: 0,
|
3407
3521
|
}
|