@webex/plugin-meetings 3.1.0-next.2 → 3.1.0-next.21
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/common/errors/{reconnection-in-progress.js → reconnection-not-started.js} +27 -15
- package/dist/common/errors/reconnection-not-started.js.map +1 -0
- package/dist/constants.js +16 -5
- 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/controlsUtils.js +7 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +10 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/media/properties.js +102 -57
- package/dist/media/properties.js.map +1 -1
- package/dist/mediaQualityMetrics/config.js +10 -10
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +6 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +543 -467
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +27 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/util.js +9 -16
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting/voicea-meeting.js +37 -49
- package/dist/meeting/voicea-meeting.js.map +1 -1
- package/dist/meetings/index.js +12 -28
- package/dist/meetings/index.js.map +1 -1
- package/dist/reachability/index.js +88 -9
- package/dist/reachability/index.js.map +1 -1
- package/dist/reconnection-manager/index.js +138 -109
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/request.js +3 -27
- package/dist/roap/request.js.map +1 -1
- package/dist/statsAnalyzer/index.js +8 -2
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +17 -0
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/common/errors/reconnection-not-started.d.ts +13 -0
- package/dist/types/constants.d.ts +13 -3
- package/dist/types/media/properties.d.ts +26 -2
- package/dist/types/mediaQualityMetrics/config.d.ts +8 -2
- package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
- package/dist/types/meeting/index.d.ts +5 -6
- package/dist/types/meeting/locusMediaRequest.d.ts +1 -0
- package/dist/types/meeting/util.d.ts +3 -0
- package/dist/types/meeting/voicea-meeting.d.ts +3 -2
- package/dist/types/meetings/index.d.ts +1 -16
- package/dist/types/reachability/index.d.ts +11 -0
- package/dist/types/reconnection-manager/index.d.ts +4 -14
- package/dist/webinar/index.js +1 -1
- package/package.json +21 -21
- package/src/common/errors/reconnection-not-started.ts +25 -0
- package/src/constants.ts +14 -5
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +16 -0
- package/src/media/properties.ts +67 -15
- package/src/mediaQualityMetrics/config.ts +13 -7
- package/src/meeting/in-meeting-actions.ts +12 -0
- package/src/meeting/index.ts +121 -98
- package/src/meeting/locusMediaRequest.ts +31 -0
- package/src/meeting/util.ts +9 -16
- package/src/meeting/voicea-meeting.ts +44 -46
- package/src/meetings/index.ts +15 -27
- package/src/reachability/index.ts +60 -0
- package/src/reconnection-manager/index.ts +128 -105
- package/src/roap/request.ts +1 -24
- package/src/statsAnalyzer/index.ts +10 -3
- package/src/statsAnalyzer/mqaUtil.ts +23 -0
- package/test/unit/spec/locus-info/controlsUtils.js +20 -0
- package/test/unit/spec/locus-info/index.js +21 -0
- package/test/unit/spec/media/properties.ts +145 -140
- package/test/unit/spec/meeting/in-meeting-actions.ts +6 -0
- package/test/unit/spec/meeting/index.js +243 -97
- package/test/unit/spec/meeting/locusMediaRequest.ts +49 -0
- package/test/unit/spec/meeting/utils.js +3 -10
- package/test/unit/spec/meeting/voicea-meeting.ts +5 -14
- package/test/unit/spec/meetings/index.js +59 -17
- package/test/unit/spec/reachability/index.ts +266 -0
- package/test/unit/spec/reconnection-manager/index.js +127 -39
- package/test/unit/spec/roap/request.ts +0 -37
- package/test/unit/spec/stats-analyzer/index.js +100 -8
- package/dist/common/errors/reconnection-in-progress.js.map +0 -1
- package/dist/types/common/errors/reconnection-in-progress.d.ts +0 -9
- package/src/common/errors/reconnection-in-progress.ts +0 -8
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {type MeetingTranscriptPayload} from '@webex/internal-plugin-voicea';
|
|
2
|
+
|
|
1
3
|
export const getSpeaker = (members, csis = []) =>
|
|
2
4
|
Object.values(members).find((member: any) => {
|
|
3
5
|
const memberCSIs = member.participant.status.csis ?? [];
|
|
@@ -31,43 +33,16 @@ export const getSpeakerFromProxyOrStore = ({csisKey, meetingMembers, transcriptD
|
|
|
31
33
|
return {speaker, needsCaching};
|
|
32
34
|
};
|
|
33
35
|
|
|
34
|
-
export const processNewCaptions = ({
|
|
36
|
+
export const processNewCaptions = ({
|
|
37
|
+
data,
|
|
38
|
+
meeting,
|
|
39
|
+
}: {
|
|
40
|
+
data: MeetingTranscriptPayload;
|
|
41
|
+
meeting: any;
|
|
42
|
+
}) => {
|
|
35
43
|
const {transcriptId} = data;
|
|
36
44
|
const transcriptData = meeting.transcription;
|
|
37
45
|
|
|
38
|
-
if (data.isFinal) {
|
|
39
|
-
transcriptData.interimCaptions[transcriptId].forEach((interimId) => {
|
|
40
|
-
const interimTranscriptIndex = transcriptData.captions.findIndex(
|
|
41
|
-
(transcript) => transcript.id === interimId
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
if (interimTranscriptIndex !== -1) {
|
|
45
|
-
transcriptData.captions.splice(interimTranscriptIndex, 1);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
delete transcriptData.interimCaptions[transcriptId];
|
|
49
|
-
const csisKey = data.transcript?.csis[0];
|
|
50
|
-
|
|
51
|
-
const {needsCaching, speaker} = getSpeakerFromProxyOrStore({
|
|
52
|
-
meetingMembers: meeting.members.membersCollection.members,
|
|
53
|
-
transcriptData,
|
|
54
|
-
csisKey,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (needsCaching) {
|
|
58
|
-
transcriptData.speakerProxy[csisKey] = speaker;
|
|
59
|
-
}
|
|
60
|
-
const captionData = {
|
|
61
|
-
id: transcriptId,
|
|
62
|
-
isFinal: data.isFinal,
|
|
63
|
-
translations: data.translations,
|
|
64
|
-
text: data.transcript?.text,
|
|
65
|
-
currentSpokenLanguage: data.transcript?.transcript_language_code,
|
|
66
|
-
timestamp: data.timestamp,
|
|
67
|
-
speaker,
|
|
68
|
-
};
|
|
69
|
-
transcriptData.captions.push(captionData);
|
|
70
|
-
}
|
|
71
46
|
const {transcripts = []} = data;
|
|
72
47
|
const transcriptsPerCsis = new Map();
|
|
73
48
|
|
|
@@ -80,8 +55,11 @@ export const processNewCaptions = ({data, meeting}) => {
|
|
|
80
55
|
|
|
81
56
|
const newCaption = `${transcriptsPerCsis.get(csisMember)?.text ?? ''} ${text}`.trim();
|
|
82
57
|
|
|
83
|
-
|
|
84
|
-
|
|
58
|
+
transcriptsPerCsis.set(csisMember, {
|
|
59
|
+
...transcript,
|
|
60
|
+
text: newCaption,
|
|
61
|
+
currentSpokenLanguage,
|
|
62
|
+
});
|
|
85
63
|
}
|
|
86
64
|
const interimTranscriptionIds = [];
|
|
87
65
|
|
|
@@ -91,31 +69,51 @@ export const processNewCaptions = ({data, meeting}) => {
|
|
|
91
69
|
transcriptData,
|
|
92
70
|
csisKey: key,
|
|
93
71
|
});
|
|
72
|
+
const {speakerId} = speaker;
|
|
73
|
+
const interimId = `${transcriptId}_${speakerId}`;
|
|
94
74
|
|
|
95
75
|
if (needsCaching) {
|
|
96
76
|
transcriptData.speakerProxy[key] = speaker;
|
|
97
77
|
}
|
|
98
|
-
|
|
99
|
-
const interimId = `${transcriptId}_${speakerId}`;
|
|
78
|
+
|
|
100
79
|
const captionData = {
|
|
101
80
|
id: interimId,
|
|
102
81
|
isFinal: data.isFinal,
|
|
103
82
|
translations: value.translations,
|
|
104
83
|
text: value.text,
|
|
105
|
-
currentCaptionLanguage:
|
|
84
|
+
currentCaptionLanguage:
|
|
85
|
+
meeting.transcription?.languageOptions?.currentCaptionLanguage ||
|
|
86
|
+
value.currentSpokenLanguage,
|
|
87
|
+
currentSpokenLanguage:
|
|
88
|
+
meeting.transcription?.languageOptions?.currentSpokenLanguage ||
|
|
89
|
+
data.transcripts[0]?.transcript_language_code,
|
|
106
90
|
timestamp: value?.timestamp,
|
|
107
91
|
speaker,
|
|
108
92
|
};
|
|
109
93
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
94
|
+
if (!data.isFinal) {
|
|
95
|
+
const interimTranscriptIndex = transcriptData.captions.findIndex(
|
|
96
|
+
(transcript) => transcript.id === interimId
|
|
97
|
+
);
|
|
113
98
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
99
|
+
if (interimTranscriptIndex !== -1) {
|
|
100
|
+
transcriptData.captions.splice(interimTranscriptIndex, 1);
|
|
101
|
+
}
|
|
117
102
|
|
|
118
|
-
|
|
103
|
+
interimTranscriptionIds.push(interimId);
|
|
104
|
+
} else {
|
|
105
|
+
transcriptData.interimCaptions[transcriptId].forEach((innerInterimId) => {
|
|
106
|
+
const interimTranscriptIndex = transcriptData.captions.findIndex(
|
|
107
|
+
(transcript) => transcript.id === innerInterimId
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (interimTranscriptIndex !== -1) {
|
|
111
|
+
transcriptData.captions.splice(interimTranscriptIndex, 1);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
delete transcriptData.interimCaptions[transcriptId];
|
|
115
|
+
captionData.id = transcriptId;
|
|
116
|
+
}
|
|
119
117
|
transcriptData.captions.push(captionData);
|
|
120
118
|
}
|
|
121
119
|
transcriptData.interimCaptions[transcriptId] = interimTranscriptionIds;
|
package/src/meetings/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
|
|
2
|
-
|
|
2
|
+
import {union} from 'lodash';
|
|
3
3
|
import '@webex/internal-plugin-mercury';
|
|
4
4
|
import '@webex/internal-plugin-conversation';
|
|
5
5
|
import '@webex/internal-plugin-metrics';
|
|
@@ -45,6 +45,8 @@ import {
|
|
|
45
45
|
MEETINGNUMBER,
|
|
46
46
|
_JOINED_,
|
|
47
47
|
_MOVED_,
|
|
48
|
+
_ON_HOLD_LOBBY_,
|
|
49
|
+
_WAIT_,
|
|
48
50
|
} from '../constants';
|
|
49
51
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
50
52
|
import MeetingInfo from '../meeting-info';
|
|
@@ -340,6 +342,9 @@ export default class Meetings extends WebexPlugin {
|
|
|
340
342
|
if (newLocus) {
|
|
341
343
|
const isNewLocusAsBreakout = MeetingsUtil.isBreakoutLocusDTO(newLocus);
|
|
342
344
|
const isSelfMoved = newLocus?.self?.state === _LEFT_ && newLocus?.self?.reason === _MOVED_;
|
|
345
|
+
const isSelfMovedToLobby =
|
|
346
|
+
newLocus?.self?.devices[0]?.intent?.reason === _ON_HOLD_LOBBY_ &&
|
|
347
|
+
newLocus?.self?.devices[0]?.intent?.type === _WAIT_;
|
|
343
348
|
if (!meeting) {
|
|
344
349
|
if (isNewLocusAsBreakout) {
|
|
345
350
|
LoggerProxy.logger.log(
|
|
@@ -352,7 +357,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
352
357
|
return this.isNeedHandleMainLocus(meeting, newLocus);
|
|
353
358
|
}
|
|
354
359
|
if (!isNewLocusAsBreakout) {
|
|
355
|
-
return this.isNeedHandleMainLocus(meeting, newLocus);
|
|
360
|
+
return isSelfMovedToLobby || this.isNeedHandleMainLocus(meeting, newLocus);
|
|
356
361
|
}
|
|
357
362
|
|
|
358
363
|
return !isSelfMoved;
|
|
@@ -996,7 +1001,10 @@ export default class Meetings extends WebexPlugin {
|
|
|
996
1001
|
fetchUserPreferredWebexSite() {
|
|
997
1002
|
return this.request.getMeetingPreferences().then((res) => {
|
|
998
1003
|
if (res) {
|
|
999
|
-
|
|
1004
|
+
const preferredWebexSite = MeetingsUtil.parseDefaultSiteFromMeetingPreferences(res);
|
|
1005
|
+
this.preferredWebexSite = preferredWebexSite;
|
|
1006
|
+
// @ts-ignore
|
|
1007
|
+
this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]);
|
|
1000
1008
|
}
|
|
1001
1009
|
|
|
1002
1010
|
// fall back to getting the preferred site from the user information
|
|
@@ -1009,6 +1017,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1009
1017
|
user?.userPreferences?.userPreferencesItems?.preferredWebExSite;
|
|
1010
1018
|
if (preferredWebexSite) {
|
|
1011
1019
|
this.preferredWebexSite = preferredWebexSite;
|
|
1020
|
+
// @ts-ignore
|
|
1021
|
+
this.webex.internal.services._getCatalog().addAllowedDomains([preferredWebexSite]);
|
|
1012
1022
|
} else {
|
|
1013
1023
|
throw new Error('site not found');
|
|
1014
1024
|
}
|
|
@@ -1383,22 +1393,12 @@ export default class Meetings extends WebexPlugin {
|
|
|
1383
1393
|
|
|
1384
1394
|
/**
|
|
1385
1395
|
* Get all meetings.
|
|
1386
|
-
* @param {object} options
|
|
1387
|
-
* @param {object} options.startDate - get meetings after this start date
|
|
1388
|
-
* @param {object} options.endDate - get meetings before this end date
|
|
1389
1396
|
* @returns {Object} All currently active meetings.
|
|
1390
1397
|
* @public
|
|
1391
1398
|
* @memberof Meetings
|
|
1392
1399
|
*/
|
|
1393
|
-
public getAllMeetings(
|
|
1394
|
-
|
|
1395
|
-
startDate: object;
|
|
1396
|
-
endDate: object;
|
|
1397
|
-
} = {} as any
|
|
1398
|
-
) {
|
|
1399
|
-
// Options may include other parameters to filter this collection
|
|
1400
|
-
// of meetings.
|
|
1401
|
-
return this.meetingCollection.getAll(options);
|
|
1400
|
+
public getAllMeetings() {
|
|
1401
|
+
return this.meetingCollection.getAll();
|
|
1402
1402
|
}
|
|
1403
1403
|
|
|
1404
1404
|
/**
|
|
@@ -1524,18 +1524,6 @@ export default class Meetings extends WebexPlugin {
|
|
|
1524
1524
|
this.breakoutLocusForHandleLater.splice(existIndex, 1);
|
|
1525
1525
|
}
|
|
1526
1526
|
|
|
1527
|
-
/**
|
|
1528
|
-
* Get all scheduled meetings.
|
|
1529
|
-
* @param {object} options
|
|
1530
|
-
* @param {object} options.startDate - get meetings after this start date
|
|
1531
|
-
* @param {object} options.endDate - get meetings before this end date
|
|
1532
|
-
* @returns {Object} All scheduled meetings.
|
|
1533
|
-
* @memberof Meetings
|
|
1534
|
-
*/
|
|
1535
|
-
getScheduledMeetings() {
|
|
1536
|
-
return this.meetingCollection.getAll({scheduled: true});
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
1527
|
/**
|
|
1540
1528
|
* Get the logger instance for plugin-meetings
|
|
1541
1529
|
* @returns {Logger}
|
|
@@ -296,6 +296,63 @@ export default class Reachability {
|
|
|
296
296
|
return reachable;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Returns true only if ALL protocols (UDP, TCP and TLS) have been tested and none
|
|
301
|
+
* of the media clusters where reachable with any of the protocols. This is done
|
|
302
|
+
* irrespective of the config, so for example:
|
|
303
|
+
* if config.meetings.experimental.enableTlsReachability === false,
|
|
304
|
+
* it will return false, because TLS reachability won't be tested,
|
|
305
|
+
* so we can't say for sure that media backend is unreachable over TLS.
|
|
306
|
+
*
|
|
307
|
+
* @returns {boolean}
|
|
308
|
+
*/
|
|
309
|
+
async isWebexMediaBackendUnreachable() {
|
|
310
|
+
let unreachable = false;
|
|
311
|
+
|
|
312
|
+
// @ts-ignore
|
|
313
|
+
const reachabilityData = await this.webex.boundedStorage
|
|
314
|
+
.get(this.namespace, REACHABILITY.localStorageResult)
|
|
315
|
+
.catch(() => {});
|
|
316
|
+
|
|
317
|
+
if (reachabilityData) {
|
|
318
|
+
try {
|
|
319
|
+
const reachabilityResults: ReachabilityResults = JSON.parse(reachabilityData);
|
|
320
|
+
|
|
321
|
+
const protocols = {
|
|
322
|
+
udp: {tested: false, reachable: undefined},
|
|
323
|
+
tcp: {tested: false, reachable: undefined},
|
|
324
|
+
xtls: {tested: false, reachable: undefined},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
Object.values(reachabilityResults).forEach((result) => {
|
|
328
|
+
Object.keys(protocols).forEach((protocol) => {
|
|
329
|
+
if (
|
|
330
|
+
result[protocol]?.result === 'reachable' ||
|
|
331
|
+
result[protocol]?.result === 'unreachable'
|
|
332
|
+
) {
|
|
333
|
+
protocols[protocol].tested = true;
|
|
334
|
+
|
|
335
|
+
// we need at least 1 'reachable' result to mark the whole protocol as reachable
|
|
336
|
+
if (result[protocol].result === 'reachable') {
|
|
337
|
+
protocols[protocol].reachable = true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
unreachable = Object.values(protocols).every(
|
|
344
|
+
(protocol) => protocol.tested && !protocol.reachable
|
|
345
|
+
);
|
|
346
|
+
} catch (e) {
|
|
347
|
+
LoggerProxy.logger.error(
|
|
348
|
+
`Roap:request#attachReachabilityData --> Error in parsing reachability data: ${e}`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return unreachable;
|
|
354
|
+
}
|
|
355
|
+
|
|
299
356
|
/**
|
|
300
357
|
* Get list of all unreachable clusters
|
|
301
358
|
* @returns {array} Unreachable clusters
|
|
@@ -314,6 +371,9 @@ export default class Reachability {
|
|
|
314
371
|
if (result.tcp.result === 'unreachable') {
|
|
315
372
|
unreachableList.push({name: key, protocol: 'tcp'});
|
|
316
373
|
}
|
|
374
|
+
if (result.xtls.result === 'unreachable') {
|
|
375
|
+
unreachableList.push({name: key, protocol: 'xtls'});
|
|
376
|
+
}
|
|
317
377
|
});
|
|
318
378
|
|
|
319
379
|
return unreachableList;
|
|
@@ -17,11 +17,11 @@ import {
|
|
|
17
17
|
RECONNECTION_STATE,
|
|
18
18
|
} from '../constants';
|
|
19
19
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
20
|
-
import
|
|
20
|
+
import ReconnectionError from '../common/errors/reconnection';
|
|
21
|
+
import ReconnectionNotStartedError from '../common/errors/reconnection-not-started';
|
|
21
22
|
import Metrics from '../metrics';
|
|
22
23
|
import Meeting from '../meeting';
|
|
23
24
|
import {MediaRequestManager} from '../multistream/mediaRequestManager';
|
|
24
|
-
import ReconnectionError from '../common/errors/reconnection';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Used to indicate that the reconnect logic needs to be retried.
|
|
@@ -73,7 +73,6 @@ export default class ReconnectionManager {
|
|
|
73
73
|
rejoinAttempts: any;
|
|
74
74
|
shareStatus: any;
|
|
75
75
|
status: any;
|
|
76
|
-
tryCount: any;
|
|
77
76
|
webex: any;
|
|
78
77
|
/**
|
|
79
78
|
* @param {Meeting} meeting
|
|
@@ -102,13 +101,6 @@ export default class ReconnectionManager {
|
|
|
102
101
|
* @memberof ReconnectionManager
|
|
103
102
|
*/
|
|
104
103
|
this.status = RECONNECTION.STATE.DEFAULT_STATUS;
|
|
105
|
-
/**
|
|
106
|
-
* @instance
|
|
107
|
-
* @type {Number}
|
|
108
|
-
* @private
|
|
109
|
-
* @memberof ReconnectionManager
|
|
110
|
-
*/
|
|
111
|
-
this.tryCount = RECONNECTION.STATE.DEFAULT_TRY_COUNT;
|
|
112
104
|
/**
|
|
113
105
|
* @instance
|
|
114
106
|
* @type {Object}
|
|
@@ -131,7 +123,7 @@ export default class ReconnectionManager {
|
|
|
131
123
|
|
|
132
124
|
// @ts-ignore
|
|
133
125
|
this.maxRejoinAttempts = meeting.config.reconnection.maxRejoinAttempts;
|
|
134
|
-
this.rejoinAttempts =
|
|
126
|
+
this.rejoinAttempts = 0;
|
|
135
127
|
// @ts-ignore
|
|
136
128
|
this.autoRejoinEnabled = meeting.config.reconnection.autoRejoin;
|
|
137
129
|
|
|
@@ -217,8 +209,7 @@ export default class ReconnectionManager {
|
|
|
217
209
|
*/
|
|
218
210
|
public reset() {
|
|
219
211
|
this.status = RECONNECTION.STATE.DEFAULT_STATUS;
|
|
220
|
-
this.
|
|
221
|
-
this.rejoinAttempts = RECONNECTION.STATE.DEFAULT_TRY_COUNT;
|
|
212
|
+
this.rejoinAttempts = 0;
|
|
222
213
|
}
|
|
223
214
|
|
|
224
215
|
/**
|
|
@@ -265,43 +256,30 @@ export default class ReconnectionManager {
|
|
|
265
256
|
return this.status === RECONNECTION.STATE.IN_PROGRESS;
|
|
266
257
|
}
|
|
267
258
|
|
|
268
|
-
/**
|
|
269
|
-
* Sets the reconnection status
|
|
270
|
-
*
|
|
271
|
-
* @public
|
|
272
|
-
* @param {RECONNECTION_STATE} status
|
|
273
|
-
* @memberof ReconnectionManager
|
|
274
|
-
* @returns {undefined}
|
|
275
|
-
*/
|
|
276
|
-
public setStatus(status: RECONNECTION_STATE) {
|
|
277
|
-
this.status = status;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
259
|
/**
|
|
281
260
|
* @returns {Boolean}
|
|
282
|
-
* @throws {
|
|
261
|
+
* @throws {ReconnectInProgress, ReconnectionDisabled}
|
|
283
262
|
* @private
|
|
284
263
|
* @memberof ReconnectionManager
|
|
285
264
|
*/
|
|
286
|
-
private
|
|
265
|
+
private canStartReconnection() {
|
|
287
266
|
if (this.meeting.config.reconnection.enabled) {
|
|
288
|
-
if (
|
|
289
|
-
this.status === RECONNECTION.STATE.DEFAULT_STATUS ||
|
|
290
|
-
this.status === RECONNECTION.STATE.COMPLETE
|
|
291
|
-
) {
|
|
267
|
+
if (this.status === RECONNECTION.STATE.DEFAULT_STATUS) {
|
|
292
268
|
return true;
|
|
293
269
|
}
|
|
294
270
|
|
|
295
271
|
LoggerProxy.logger.info(
|
|
296
|
-
'ReconnectionManager:index#
|
|
272
|
+
'ReconnectionManager:index#canStartReconnection --> Reconnection already in progress.'
|
|
297
273
|
);
|
|
298
274
|
|
|
299
|
-
|
|
275
|
+
return false;
|
|
300
276
|
}
|
|
301
277
|
|
|
302
|
-
LoggerProxy.logger.info(
|
|
278
|
+
LoggerProxy.logger.info(
|
|
279
|
+
'ReconnectionManager:index#canStartReconnection --> Reconnection is not enabled.'
|
|
280
|
+
);
|
|
303
281
|
|
|
304
|
-
|
|
282
|
+
return false;
|
|
305
283
|
}
|
|
306
284
|
|
|
307
285
|
/**
|
|
@@ -309,105 +287,152 @@ export default class ReconnectionManager {
|
|
|
309
287
|
* @param {Object} reconnectOptions
|
|
310
288
|
* @param {boolean} [reconnectOptions.networkDisconnect=false] indicates if a network disconnect event happened
|
|
311
289
|
* @param {boolean} [reconnectOptions.networkRetry=false] indicates if we are retrying the reconnect
|
|
290
|
+
* @param {Function} [completionCallback] callback that gets called when reconnection is started successfully
|
|
312
291
|
* @returns {Promise}
|
|
313
292
|
* @public
|
|
314
293
|
* @memberof ReconnectionManager
|
|
315
294
|
*/
|
|
316
|
-
public async reconnect(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
295
|
+
public async reconnect(
|
|
296
|
+
{
|
|
297
|
+
networkDisconnect = false,
|
|
298
|
+
networkRetry = false,
|
|
299
|
+
}: {
|
|
300
|
+
networkDisconnect?: boolean;
|
|
301
|
+
networkRetry?: boolean;
|
|
302
|
+
} = {},
|
|
303
|
+
completionCallback: (() => Promise<void>) | undefined = undefined
|
|
304
|
+
) {
|
|
323
305
|
LoggerProxy.logger.info(
|
|
324
306
|
`ReconnectionManager:index#reconnect --> Reconnection start for meeting ${this.meeting.id}.`
|
|
325
307
|
);
|
|
326
|
-
// First, validate that we can reconnect, if not, it will throw an error
|
|
327
|
-
try {
|
|
328
|
-
this.validate();
|
|
329
|
-
} catch (error) {
|
|
330
|
-
LoggerProxy.logger.info(
|
|
331
|
-
'ReconnectionManager:index#reconnect --> Reconnection unable to begin.',
|
|
332
|
-
error
|
|
333
|
-
);
|
|
334
|
-
throw error;
|
|
335
|
-
}
|
|
336
308
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
309
|
+
const triggerEvent = (event, payload = undefined) =>
|
|
310
|
+
Trigger.trigger(
|
|
311
|
+
this.meeting,
|
|
312
|
+
{
|
|
313
|
+
file: 'reconnection-manager/index',
|
|
314
|
+
function: 'reconnect',
|
|
315
|
+
},
|
|
316
|
+
event,
|
|
317
|
+
payload
|
|
341
318
|
);
|
|
342
319
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
name: 'client.media.reconnecting',
|
|
346
|
-
options: {
|
|
347
|
-
meetingId: this.meeting.id,
|
|
348
|
-
},
|
|
349
|
-
});
|
|
320
|
+
if (!this.canStartReconnection()) {
|
|
321
|
+
throw new ReconnectionNotStartedError();
|
|
350
322
|
}
|
|
351
323
|
|
|
352
324
|
try {
|
|
353
|
-
|
|
354
|
-
} catch (err) {
|
|
355
|
-
LoggerProxy.logger.info(
|
|
356
|
-
'ReconnectionManager:index#reconnect --> Reachability failed, continuing with reconnection attempt, err: ',
|
|
357
|
-
err
|
|
358
|
-
);
|
|
359
|
-
}
|
|
325
|
+
this.status = RECONNECTION.STATE.IN_PROGRESS;
|
|
360
326
|
|
|
361
|
-
|
|
362
|
-
const media = await this.executeReconnection({networkDisconnect});
|
|
327
|
+
triggerEvent(EVENT_TRIGGERS.MEETING_RECONNECTION_STARTING);
|
|
363
328
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (reconnectError instanceof NeedsRetryError) {
|
|
329
|
+
if (!networkRetry) {
|
|
330
|
+
// Only log START metrics on the initial reconnect
|
|
367
331
|
LoggerProxy.logger.info(
|
|
368
|
-
'ReconnectionManager:index#reconnect -->
|
|
332
|
+
'ReconnectionManager:index#reconnect --> Sending reconnect start metric.'
|
|
369
333
|
);
|
|
370
|
-
// Reset our reconnect status since we are looping back to the beginning
|
|
371
|
-
this.status = RECONNECTION.STATE.DEFAULT_STATUS;
|
|
372
334
|
|
|
373
|
-
//
|
|
374
|
-
|
|
335
|
+
// @ts-ignore
|
|
336
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
337
|
+
name: 'client.media.reconnecting',
|
|
338
|
+
options: {
|
|
339
|
+
meetingId: this.meeting.id,
|
|
340
|
+
},
|
|
341
|
+
});
|
|
375
342
|
}
|
|
376
343
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
344
|
+
try {
|
|
345
|
+
await this.webex.meetings.startReachability();
|
|
346
|
+
} catch (err) {
|
|
347
|
+
LoggerProxy.logger.info(
|
|
348
|
+
'ReconnectionManager:index#reconnect --> Reachability failed, continuing with reconnection attempt, err: ',
|
|
349
|
+
err
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
await this.executeReconnection({networkDisconnect});
|
|
355
|
+
} catch (reconnectError) {
|
|
356
|
+
if (reconnectError instanceof NeedsRetryError) {
|
|
357
|
+
LoggerProxy.logger.info(
|
|
358
|
+
'ReconnectionManager:index#reconnect --> Reconnection not successful, retrying.'
|
|
359
|
+
);
|
|
360
|
+
// Reset our reconnect status since we are looping back to the beginning
|
|
361
|
+
this.status = RECONNECTION.STATE.DEFAULT_STATUS;
|
|
362
|
+
|
|
363
|
+
// This is a network retry, so we should not log START metrics again
|
|
364
|
+
await this.reconnect({networkDisconnect: true, networkRetry: true}, completionCallback);
|
|
365
|
+
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Reconnect has failed
|
|
370
|
+
LoggerProxy.logger.error(
|
|
371
|
+
'ReconnectionManager:index#reconnect --> Reconnection failed.',
|
|
372
|
+
reconnectError.message
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
// send call aborted event with category as expected as we are trying to rejoin
|
|
376
|
+
// @ts-ignore
|
|
377
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
378
|
+
name: 'client.call.aborted',
|
|
379
|
+
payload: {
|
|
380
|
+
errors: [
|
|
381
|
+
{
|
|
382
|
+
category: 'expected',
|
|
383
|
+
errorCode: 2008,
|
|
384
|
+
fatal: true,
|
|
385
|
+
name: 'media-engine',
|
|
386
|
+
shownToUser: false,
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
options: {
|
|
391
|
+
meetingId: this.meeting.id,
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
if (reconnectError instanceof NeedsRejoinError && this.autoRejoinEnabled) {
|
|
396
|
+
await this.rejoinMeeting(reconnectError.wasSharing);
|
|
397
|
+
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
throw reconnectError;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// finalize the reconnection process by calling the completionCallback
|
|
405
|
+
if (completionCallback) {
|
|
406
|
+
await completionCallback();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
triggerEvent(EVENT_TRIGGERS.MEETING_RECONNECTION_SUCCESS);
|
|
385
410
|
|
|
386
|
-
// send call aborted event with catogery as expected as we are trying to rejoin
|
|
387
411
|
// @ts-ignore
|
|
388
412
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
389
|
-
name: 'client.
|
|
413
|
+
name: 'client.media.recovered',
|
|
390
414
|
payload: {
|
|
391
|
-
|
|
392
|
-
{
|
|
393
|
-
category: 'expected',
|
|
394
|
-
errorCode: 2008,
|
|
395
|
-
fatal: true,
|
|
396
|
-
name: 'media-engine',
|
|
397
|
-
shownToUser: false,
|
|
398
|
-
},
|
|
399
|
-
],
|
|
415
|
+
recoveredBy: 'new',
|
|
400
416
|
},
|
|
401
417
|
options: {
|
|
402
418
|
meetingId: this.meeting.id,
|
|
403
419
|
},
|
|
404
420
|
});
|
|
421
|
+
} catch (error) {
|
|
422
|
+
triggerEvent(EVENT_TRIGGERS.MEETING_RECONNECTION_FAILURE, {
|
|
423
|
+
error: new ReconnectionError('Reconnection failure event', error),
|
|
424
|
+
});
|
|
405
425
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
426
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_RECONNECT_FAILURE, {
|
|
427
|
+
correlation_id: this.meeting.correlationId,
|
|
428
|
+
locus_id: this.meeting.locusUrl.split('/').pop(),
|
|
429
|
+
reason: error.message,
|
|
430
|
+
stack: error.stack,
|
|
431
|
+
});
|
|
409
432
|
|
|
410
|
-
throw
|
|
433
|
+
throw new ReconnectionError('Reconnection failure event', error);
|
|
434
|
+
} finally {
|
|
435
|
+
this.reset();
|
|
411
436
|
}
|
|
412
437
|
}
|
|
413
438
|
|
|
@@ -420,8 +445,6 @@ export default class ReconnectionManager {
|
|
|
420
445
|
* @memberof ReconnectionManager
|
|
421
446
|
*/
|
|
422
447
|
private async executeReconnection({networkDisconnect = false}: {networkDisconnect?: boolean}) {
|
|
423
|
-
this.status = RECONNECTION.STATE.IN_PROGRESS;
|
|
424
|
-
|
|
425
448
|
LoggerProxy.logger.info(
|
|
426
449
|
'ReconnectionManager:index#executeReconnection --> Attempting to reconnect to meeting.'
|
|
427
450
|
);
|
package/src/roap/request.ts
CHANGED
|
@@ -61,7 +61,7 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
61
61
|
ipVersion?: IP_VERSION;
|
|
62
62
|
locusMediaRequest?: LocusMediaRequest;
|
|
63
63
|
}) {
|
|
64
|
-
const {roapMessage, locusSelfUrl, mediaId,
|
|
64
|
+
const {roapMessage, locusSelfUrl, mediaId, locusMediaRequest, ipVersion} = options;
|
|
65
65
|
|
|
66
66
|
if (!mediaId) {
|
|
67
67
|
LoggerProxy.logger.info('Roap:request#sendRoap --> sending empty mediaID');
|
|
@@ -82,14 +82,6 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
82
82
|
`Roap:request#sendRoap --> ${locusSelfUrl} \n ${roapMessage.messageType} \n seq:${roapMessage.seq}`
|
|
83
83
|
);
|
|
84
84
|
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
87
|
-
name: 'client.locus.media.request',
|
|
88
|
-
options: {
|
|
89
|
-
meetingId,
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
85
|
return locusMediaRequest
|
|
94
86
|
.send({
|
|
95
87
|
type: 'RoapMessage',
|
|
@@ -101,13 +93,6 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
101
93
|
ipVersion,
|
|
102
94
|
})
|
|
103
95
|
.then((res) => {
|
|
104
|
-
// @ts-ignore
|
|
105
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
106
|
-
name: 'client.locus.media.response',
|
|
107
|
-
options: {
|
|
108
|
-
meetingId,
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
96
|
// always it will be the first mediaConnection Object
|
|
112
97
|
const mediaConnections =
|
|
113
98
|
res.body.mediaConnections &&
|
|
@@ -131,14 +116,6 @@ export default class RoapRequest extends StatelessWebexPlugin {
|
|
|
131
116
|
};
|
|
132
117
|
})
|
|
133
118
|
.catch((err) => {
|
|
134
|
-
// @ts-ignore
|
|
135
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
136
|
-
name: 'client.locus.media.response',
|
|
137
|
-
options: {
|
|
138
|
-
meetingId,
|
|
139
|
-
rawError: err,
|
|
140
|
-
},
|
|
141
|
-
});
|
|
142
119
|
LoggerProxy.logger.error(`Roap:request#sendRoap --> Error:`, err);
|
|
143
120
|
LoggerProxy.logger.error(
|
|
144
121
|
`Roap:request#sendRoapRequest --> roapMessage that caused error:${JSON.stringify(
|