@webex/plugin-meetings 1.160.0 → 2.1.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/README.md +1 -0
- package/dist/constants.js +10 -2
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +10 -3
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +11 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/index.js +190 -67
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +17 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/state.js +5 -2
- package/dist/meeting/state.js.map +1 -1
- package/dist/meeting/util.js +23 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +66 -16
- package/dist/meetings/index.js.map +1 -1
- package/dist/metrics/constants.js +2 -0
- package/dist/metrics/constants.js.map +1 -1
- package/package.json +6 -6
- package/src/constants.js +6 -0
- package/src/locus-info/controlsUtils.js +11 -1
- package/src/locus-info/index.js +23 -1
- package/src/meeting/index.js +153 -26
- package/src/meeting/request.js +18 -0
- package/src/meeting/state.js +12 -1
- package/src/meeting/util.js +31 -1
- package/src/meetings/index.js +35 -8
- package/src/metrics/constants.js +2 -0
- package/test/unit/spec/locus-info/index.js +51 -1
- package/test/unit/spec/meeting/index.js +224 -7
- package/test/unit/spec/meeting/request.js +15 -0
- package/test/unit/spec/meetings/index.js +187 -19
package/src/meeting/index.js
CHANGED
|
@@ -646,6 +646,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
646
646
|
* @memberof Meeting
|
|
647
647
|
*/
|
|
648
648
|
this.mediaConnections = null;
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Fetching meeting info can be done randomly 2-5 mins before meeting start
|
|
652
|
+
* In case it is done before the timer expires, this timeout id is reset to cancel the timer.
|
|
653
|
+
* @instance
|
|
654
|
+
* @type {Number}
|
|
655
|
+
* @readonly
|
|
656
|
+
* @private
|
|
657
|
+
* @memberof Meeting
|
|
658
|
+
*/
|
|
659
|
+
this.fetchMeetingInfoTimeoutId = null;
|
|
660
|
+
|
|
649
661
|
/**
|
|
650
662
|
* Update the MediaConnections property with new information
|
|
651
663
|
* @param {array} mediaConnections
|
|
@@ -908,15 +920,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
908
920
|
/**
|
|
909
921
|
* Fetches meeting information.
|
|
910
922
|
* @param {Object} options
|
|
911
|
-
* @param {String} options.
|
|
912
|
-
* @param {String} options.
|
|
913
|
-
* @
|
|
923
|
+
* @param {String} [options.password] optional
|
|
924
|
+
* @param {String} [options.captchaCode] optional
|
|
925
|
+
* @public
|
|
914
926
|
* @memberof Meeting
|
|
915
927
|
* @returns {Promise}
|
|
916
928
|
*/
|
|
917
929
|
async fetchMeetingInfo({
|
|
918
930
|
password = null, captchaCode = null
|
|
919
931
|
}) {
|
|
932
|
+
// when fetch meeting info is called directly by the client, we want to clear out the random timer for sdk to do it
|
|
933
|
+
if (this.fetchMeetingInfoTimeoutId) {
|
|
934
|
+
clearTimeout(this.fetchMeetingInfoTimeoutId);
|
|
935
|
+
this.fetchMeetingInfoTimeoutId = undefined;
|
|
936
|
+
}
|
|
920
937
|
if (captchaCode && !this.requiredCaptcha) {
|
|
921
938
|
return Promise.reject(new Error('fetchMeetingInfo() called with captchaCode when captcha was not required'));
|
|
922
939
|
}
|
|
@@ -929,7 +946,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
929
946
|
|
|
930
947
|
const info = await this.attrs.meetingInfoProvider.fetchMeetingInfo(this.destination, this.destinationType, password, captchaInfo);
|
|
931
948
|
|
|
932
|
-
this.parseMeetingInfo(info);
|
|
949
|
+
this.parseMeetingInfo(info, this.destination);
|
|
933
950
|
this.meetingInfo = info ? info.body : null;
|
|
934
951
|
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NONE;
|
|
935
952
|
this.requiredCaptcha = null;
|
|
@@ -940,6 +957,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
940
957
|
this.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
|
|
941
958
|
}
|
|
942
959
|
|
|
960
|
+
Trigger.trigger(
|
|
961
|
+
this,
|
|
962
|
+
{
|
|
963
|
+
file: 'meetings',
|
|
964
|
+
function: 'fetchMeetingInfo'
|
|
965
|
+
},
|
|
966
|
+
EVENT_TRIGGERS.MEETING_INFO_AVAILABLE
|
|
967
|
+
);
|
|
968
|
+
|
|
943
969
|
return Promise.resolve();
|
|
944
970
|
}
|
|
945
971
|
catch (err) {
|
|
@@ -1485,6 +1511,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1485
1511
|
* meeting:recording:stopped
|
|
1486
1512
|
* meeting:recording:paused
|
|
1487
1513
|
* meeting:recording:resumed
|
|
1514
|
+
*
|
|
1515
|
+
* Set up the locus info meeeting container listener
|
|
1516
|
+
* update meetingContainerUrl value for the meeting
|
|
1517
|
+
* notifies consumer with:
|
|
1518
|
+
* meeting:meetingContainer:update
|
|
1519
|
+
*
|
|
1488
1520
|
* @returns {undefined}
|
|
1489
1521
|
* @private
|
|
1490
1522
|
* @memberof Meeting
|
|
@@ -1529,6 +1561,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1529
1561
|
this.recording
|
|
1530
1562
|
);
|
|
1531
1563
|
});
|
|
1564
|
+
|
|
1565
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_CONTAINER_UPDATED,
|
|
1566
|
+
({meetingContainerUrl}) => {
|
|
1567
|
+
Trigger.trigger(
|
|
1568
|
+
this,
|
|
1569
|
+
{
|
|
1570
|
+
file: 'meeting/index',
|
|
1571
|
+
function: 'setupLocusControlsListener'
|
|
1572
|
+
},
|
|
1573
|
+
EVENT_TRIGGERS.MEETING_MEETING_CONTAINER_UPDATE,
|
|
1574
|
+
{meetingContainerUrl}
|
|
1575
|
+
);
|
|
1576
|
+
});
|
|
1532
1577
|
}
|
|
1533
1578
|
|
|
1534
1579
|
/**
|
|
@@ -2355,24 +2400,32 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2355
2400
|
* @param {String} meetingInfo.body.locusUrl
|
|
2356
2401
|
* @param {String} meetingInfo.body.sipUri
|
|
2357
2402
|
* @param {Object} meetingInfo.body.owner
|
|
2403
|
+
* @param {Object | String} destination locus object with meeting data or destination string (sip url, meeting link, etc)
|
|
2358
2404
|
* @returns {undefined}
|
|
2359
2405
|
* @private
|
|
2360
2406
|
* @memberof Meeting
|
|
2361
2407
|
*/
|
|
2362
|
-
parseMeetingInfo(meetingInfo) {
|
|
2408
|
+
parseMeetingInfo(meetingInfo, destination = null) {
|
|
2363
2409
|
const webexMeetingInfo = meetingInfo?.body;
|
|
2410
|
+
// We try to use as much info from Locus meeting object, stored in destination
|
|
2411
|
+
|
|
2412
|
+
let locusMeetingObject;
|
|
2413
|
+
|
|
2414
|
+
if (destination) {
|
|
2415
|
+
locusMeetingObject = typeof destination === 'object' ? destination : undefined;
|
|
2416
|
+
}
|
|
2364
2417
|
|
|
2365
2418
|
// MeetingInfo will be undefined for 1:1 calls
|
|
2366
|
-
if (webexMeetingInfo && !(meetingInfo
|
|
2367
|
-
this.conversationUrl = webexMeetingInfo
|
|
2368
|
-
this.locusUrl = webexMeetingInfo
|
|
2369
|
-
this.setSipUri(this.config.experimental.enableUnifiedMeetings ? webexMeetingInfo
|
|
2419
|
+
if (locusMeetingObject || (webexMeetingInfo && !(meetingInfo?.errors && meetingInfo?.errors.length > 0))) {
|
|
2420
|
+
this.conversationUrl = locusMeetingObject?.conversationUrl || webexMeetingInfo?.conversationUrl || this.conversationUrl;
|
|
2421
|
+
this.locusUrl = locusMeetingObject?.url || webexMeetingInfo?.locusUrl || this.locusUrl;
|
|
2422
|
+
this.setSipUri(this.config.experimental.enableUnifiedMeetings ? locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipUrl : locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipMeetingUri || this.sipUri);
|
|
2370
2423
|
if (this.config.experimental.enableUnifiedMeetings) {
|
|
2371
|
-
this.meetingNumber = webexMeetingInfo
|
|
2372
|
-
this.meetingJoinUrl = webexMeetingInfo
|
|
2424
|
+
this.meetingNumber = locusMeetingObject?.info.webExMeetingId || webexMeetingInfo?.meetingNumber;
|
|
2425
|
+
this.meetingJoinUrl = webexMeetingInfo?.meetingJoinUrl;
|
|
2373
2426
|
}
|
|
2374
|
-
this.owner =
|
|
2375
|
-
this.permissionToken = webexMeetingInfo
|
|
2427
|
+
this.owner = locusMeetingObject?.info.owner || webexMeetingInfo?.owner || webexMeetingInfo?.hostId || this.owner;
|
|
2428
|
+
this.permissionToken = webexMeetingInfo?.permissionToken;
|
|
2376
2429
|
}
|
|
2377
2430
|
}
|
|
2378
2431
|
|
|
@@ -4737,19 +4790,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4737
4790
|
return MeetingUtil.leaveMeeting(this, options)
|
|
4738
4791
|
.then((leave) => {
|
|
4739
4792
|
this.meetingFiniteStateMachine.leave();
|
|
4740
|
-
this.
|
|
4741
|
-
this.video = null;
|
|
4742
|
-
this.isSharing = false;
|
|
4743
|
-
if (this.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE) {
|
|
4744
|
-
this.shareStatus = SHARE_STATUS.NO_SHARE;
|
|
4745
|
-
}
|
|
4746
|
-
this.queuedMediaUpdates = [];
|
|
4747
|
-
|
|
4748
|
-
if (this.transcription) {
|
|
4749
|
-
this.transcription.closeSocket();
|
|
4750
|
-
this.triggerStopReceivingTranscriptionEvent();
|
|
4751
|
-
this.transcription = undefined;
|
|
4752
|
-
}
|
|
4793
|
+
this.clearMeetingData();
|
|
4753
4794
|
|
|
4754
4795
|
// upload logs on leave irrespective of meeting delete
|
|
4755
4796
|
Trigger.trigger(
|
|
@@ -5635,4 +5676,90 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5635
5676
|
|
|
5636
5677
|
return (start && end) ? end - start : undefined;
|
|
5637
5678
|
}
|
|
5679
|
+
|
|
5680
|
+
|
|
5681
|
+
/**
|
|
5682
|
+
* End the current meeting for all
|
|
5683
|
+
* @returns {Promise}
|
|
5684
|
+
* @public
|
|
5685
|
+
* @memberof Meeting
|
|
5686
|
+
*/
|
|
5687
|
+
endMeetingForAll() {
|
|
5688
|
+
Metrics.postEvent({event: eventType.LEAVE, meeting: this, data: {trigger: trigger.USER_INTERACTION, canProceed: false}});
|
|
5689
|
+
|
|
5690
|
+
LoggerProxy.logger.log('Meeting:index#endMeetingForAll --> End meeting for All');
|
|
5691
|
+
Metrics.sendBehavioralMetric(
|
|
5692
|
+
BEHAVIORAL_METRICS.MEETING_END_ALL_INITIATED,
|
|
5693
|
+
{
|
|
5694
|
+
correlation_id: this.correlationId,
|
|
5695
|
+
locus_id: this.locusId
|
|
5696
|
+
}
|
|
5697
|
+
);
|
|
5698
|
+
|
|
5699
|
+
return MeetingUtil.endMeetingForAll(this)
|
|
5700
|
+
.then((end) => {
|
|
5701
|
+
this.meetingFiniteStateMachine.end();
|
|
5702
|
+
|
|
5703
|
+
this.clearMeetingData();
|
|
5704
|
+
// upload logs on leave irrespective of meeting delete
|
|
5705
|
+
Trigger.trigger(
|
|
5706
|
+
this,
|
|
5707
|
+
{
|
|
5708
|
+
file: 'meeting/index',
|
|
5709
|
+
function: 'endMeetingForAll'
|
|
5710
|
+
},
|
|
5711
|
+
EVENTS.REQUEST_UPLOAD_LOGS,
|
|
5712
|
+
this
|
|
5713
|
+
);
|
|
5714
|
+
|
|
5715
|
+
return end;
|
|
5716
|
+
}).catch((error) => {
|
|
5717
|
+
this.meetingFiniteStateMachine.fail(error);
|
|
5718
|
+
LoggerProxy.logger.error('Meeting:index#endMeetingForAll --> Failed to end meeting ', error);
|
|
5719
|
+
// upload logs on leave irrespective of meeting delete
|
|
5720
|
+
Trigger.trigger(
|
|
5721
|
+
this,
|
|
5722
|
+
{
|
|
5723
|
+
file: 'meeting/index',
|
|
5724
|
+
function: 'endMeetingForAll'
|
|
5725
|
+
},
|
|
5726
|
+
EVENTS.REQUEST_UPLOAD_LOGS,
|
|
5727
|
+
this
|
|
5728
|
+
);
|
|
5729
|
+
Metrics.sendBehavioralMetric(
|
|
5730
|
+
BEHAVIORAL_METRICS.MEETING_END_ALL_FAILURE,
|
|
5731
|
+
{
|
|
5732
|
+
correlation_id: this.correlationId,
|
|
5733
|
+
locus_id: this.locusUrl.split('/').pop(),
|
|
5734
|
+
reason: error.message,
|
|
5735
|
+
stack: error.stack,
|
|
5736
|
+
code: error.code
|
|
5737
|
+
}
|
|
5738
|
+
);
|
|
5739
|
+
|
|
5740
|
+
return Promise.reject(error);
|
|
5741
|
+
});
|
|
5742
|
+
}
|
|
5743
|
+
|
|
5744
|
+
/**
|
|
5745
|
+
* clear the meeting data
|
|
5746
|
+
* @returns {undefined}
|
|
5747
|
+
* @public
|
|
5748
|
+
* @memberof Meeting
|
|
5749
|
+
*/
|
|
5750
|
+
clearMeetingData = () => {
|
|
5751
|
+
this.audio = null;
|
|
5752
|
+
this.video = null;
|
|
5753
|
+
this.isSharing = false;
|
|
5754
|
+
if (this.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE) {
|
|
5755
|
+
this.shareStatus = SHARE_STATUS.NO_SHARE;
|
|
5756
|
+
}
|
|
5757
|
+
this.queuedMediaUpdates = [];
|
|
5758
|
+
|
|
5759
|
+
if (this.transcription) {
|
|
5760
|
+
this.transcription.closeSocket();
|
|
5761
|
+
this.triggerStopReceivingTranscriptionEvent();
|
|
5762
|
+
this.transcription = undefined;
|
|
5763
|
+
}
|
|
5764
|
+
};
|
|
5638
5765
|
}
|
package/src/meeting/request.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CALL,
|
|
11
11
|
CONTROLS,
|
|
12
12
|
DECLINE,
|
|
13
|
+
END,
|
|
13
14
|
FLOOR_ACTION,
|
|
14
15
|
HTTP_VERBS,
|
|
15
16
|
LEAVE,
|
|
@@ -647,4 +648,21 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
647
648
|
}
|
|
648
649
|
});
|
|
649
650
|
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Make a network request to end meeting for all
|
|
654
|
+
* @param {Object} options
|
|
655
|
+
* @param {Url} options.locusUrl
|
|
656
|
+
* @returns {Promise}
|
|
657
|
+
*/
|
|
658
|
+
endMeetingForAll({
|
|
659
|
+
locusUrl,
|
|
660
|
+
}) {
|
|
661
|
+
const uri = `${locusUrl}/${END}`;
|
|
662
|
+
|
|
663
|
+
return this.request({
|
|
664
|
+
method: HTTP_VERBS.POST,
|
|
665
|
+
uri
|
|
666
|
+
});
|
|
667
|
+
}
|
|
650
668
|
}
|
package/src/meeting/state.js
CHANGED
|
@@ -71,7 +71,18 @@ const MeetingStateMachine = {
|
|
|
71
71
|
],
|
|
72
72
|
to: MEETING_STATE_MACHINE.STATES.ENDED
|
|
73
73
|
},
|
|
74
|
-
|
|
74
|
+
{
|
|
75
|
+
name: MEETING_STATE_MACHINE.TRANSITIONS.END,
|
|
76
|
+
from: [
|
|
77
|
+
MEETING_STATE_MACHINE.STATES.IDLE,
|
|
78
|
+
MEETING_STATE_MACHINE.STATES.RINGING,
|
|
79
|
+
MEETING_STATE_MACHINE.STATES.JOINED,
|
|
80
|
+
MEETING_STATE_MACHINE.STATES.ANSWERED,
|
|
81
|
+
MEETING_STATE_MACHINE.STATES.DECLINED,
|
|
82
|
+
MEETING_STATE_MACHINE.STATES.ERROR
|
|
83
|
+
],
|
|
84
|
+
to: MEETING_STATE_MACHINE.STATES.ENDED
|
|
85
|
+
},
|
|
75
86
|
{
|
|
76
87
|
name: MEETING_STATE_MACHINE.TRANSITIONS.DECLINE,
|
|
77
88
|
from: [MEETING_STATE_MACHINE.STATES.RINGING, MEETING_STATE_MACHINE.STATES.ERROR],
|
package/src/meeting/util.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
STATS,
|
|
16
16
|
EVENT_TRIGGERS,
|
|
17
17
|
FULL_STATE,
|
|
18
|
-
PASSWORD_STATUS
|
|
18
|
+
PASSWORD_STATUS,
|
|
19
19
|
} from '../constants';
|
|
20
20
|
import Trigger from '../common/events/trigger-proxy';
|
|
21
21
|
import IntentToJoinError from '../common/errors/intent-to-join';
|
|
@@ -744,4 +744,34 @@ MeetingUtil.handleDeviceLogging = (devices = []) => {
|
|
|
744
744
|
});
|
|
745
745
|
};
|
|
746
746
|
|
|
747
|
+
MeetingUtil.endMeetingForAll = (meeting) => {
|
|
748
|
+
if (meeting.meetingState === FULL_STATE.INACTIVE) {
|
|
749
|
+
return Promise.reject(new MeetingNotActiveError());
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const endOptions = {
|
|
753
|
+
locusUrl: meeting.locusUrl,
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
return meeting.meetingRequest
|
|
757
|
+
.endMeetingForAll(endOptions)
|
|
758
|
+
.then((response) => {
|
|
759
|
+
if (response && response.body && response.body.locus) {
|
|
760
|
+
meeting.locusInfo.onFullLocus(response.body.locus);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return Promise.resolve();
|
|
764
|
+
})
|
|
765
|
+
.then(() => MeetingUtil.cleanUp(meeting))
|
|
766
|
+
.catch((err) => {
|
|
767
|
+
LoggerProxy.logger.error(
|
|
768
|
+
`Meeting:util#endMeetingForAll An error occured while trying to end meeting for all with an id of ${
|
|
769
|
+
meeting.id
|
|
770
|
+
}, error: ${err}`
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
return Promise.reject(err);
|
|
774
|
+
});
|
|
775
|
+
};
|
|
776
|
+
|
|
747
777
|
export default MeetingUtil;
|
package/src/meetings/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
READY,
|
|
22
22
|
LOCUSEVENT,
|
|
23
23
|
LOCUS_URL,
|
|
24
|
+
MAX_RANDOM_DELAY_FOR_MEETING_INFO,
|
|
24
25
|
ROAP,
|
|
25
26
|
ONLINE,
|
|
26
27
|
OFFLINE,
|
|
@@ -193,12 +194,13 @@ export default class Meetings extends WebexPlugin {
|
|
|
193
194
|
* @param {Object} data a locus event
|
|
194
195
|
* @param {String} data.locusUrl
|
|
195
196
|
* @param {Object} data.locus
|
|
197
|
+
* @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
|
|
196
198
|
* @param {String} data.eventType
|
|
197
199
|
* @returns {undefined}
|
|
198
200
|
* @private
|
|
199
201
|
* @memberof Meetings
|
|
200
202
|
*/
|
|
201
|
-
handleLocusEvent(data) {
|
|
203
|
+
handleLocusEvent(data, useRandomDelayForInfo = false) {
|
|
202
204
|
let meeting = null;
|
|
203
205
|
|
|
204
206
|
// getting meeting by correlationId. This will happen for the new event
|
|
@@ -255,7 +257,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
255
257
|
return;
|
|
256
258
|
}
|
|
257
259
|
|
|
258
|
-
this.create(data.locus, _LOCUS_ID_).then((newMeeting) => {
|
|
260
|
+
this.create(data.locus, _LOCUS_ID_, useRandomDelayForInfo).then((newMeeting) => {
|
|
259
261
|
meeting = newMeeting;
|
|
260
262
|
|
|
261
263
|
// It's a new meeting so initialize the locus data
|
|
@@ -307,7 +309,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
307
309
|
const {eventType} = data;
|
|
308
310
|
|
|
309
311
|
if (eventType && eventType !== LOCUSEVENT.MESSAGE_ROAP) {
|
|
310
|
-
this.handleLocusEvent(data);
|
|
312
|
+
this.handleLocusEvent(data, true);
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
315
|
|
|
@@ -684,11 +686,12 @@ export default class Meetings extends WebexPlugin {
|
|
|
684
686
|
* Create a meeting.
|
|
685
687
|
* @param {string} destination - sipURL, spaceId, phonenumber, or locus object}
|
|
686
688
|
* @param {string} [type] - the optional specified type, such as locusId
|
|
689
|
+
* @param {Boolean} useRandomDelayForInfo - whether a random delay should be added to fetching meeting info
|
|
687
690
|
* @returns {Promise<Meeting>} A new Meeting.
|
|
688
691
|
* @public
|
|
689
692
|
* @memberof Meetings
|
|
690
693
|
*/
|
|
691
|
-
create(destination, type = null) {
|
|
694
|
+
create(destination, type = null, useRandomDelayForInfo = false) {
|
|
692
695
|
// TODO: type should be from a dictionary
|
|
693
696
|
|
|
694
697
|
// Validate meeting information based on the provided destination and
|
|
@@ -726,11 +729,10 @@ export default class Meetings extends WebexPlugin {
|
|
|
726
729
|
meeting = this.meetingCollection.getByKey(SIP_URI, targetDest);
|
|
727
730
|
}
|
|
728
731
|
|
|
729
|
-
|
|
730
732
|
// Validate if a meeting was found.
|
|
731
733
|
if (!meeting) {
|
|
732
734
|
// Create a meeting based on the normalized destination and type.
|
|
733
|
-
return this.createMeeting(targetDest, type)
|
|
735
|
+
return this.createMeeting(targetDest, type, useRandomDelayForInfo)
|
|
734
736
|
.then((createdMeeting) => {
|
|
735
737
|
// If the meeting was successfully created.
|
|
736
738
|
if (createdMeeting && createdMeeting.on) {
|
|
@@ -779,11 +781,12 @@ export default class Meetings extends WebexPlugin {
|
|
|
779
781
|
/**
|
|
780
782
|
* @param {String} destination see create()
|
|
781
783
|
* @param {String} type see create()
|
|
784
|
+
* @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
|
|
782
785
|
* @returns {Promise} a new meeting instance complete with meeting info and destination
|
|
783
786
|
* @private
|
|
784
787
|
* @memberof Meetings
|
|
785
788
|
*/
|
|
786
|
-
async createMeeting(destination, type = null) {
|
|
789
|
+
async createMeeting(destination, type = null, useRandomDelayForInfo = false) {
|
|
787
790
|
const meeting = new Meeting(
|
|
788
791
|
{
|
|
789
792
|
userId: this.webex.internal.device.userId,
|
|
@@ -803,7 +806,31 @@ export default class Meetings extends WebexPlugin {
|
|
|
803
806
|
this.meetingCollection.set(meeting);
|
|
804
807
|
|
|
805
808
|
try {
|
|
806
|
-
|
|
809
|
+
// if no participant has joined the scheduled meeting (meaning meeting is not active) and we get a locusEvent,
|
|
810
|
+
// it means the meeting will start in 5-6 min. In that case, we want to fetchMeetingInfo
|
|
811
|
+
// between 5 and 2 min (random between 3 minutes) before the meeting starts
|
|
812
|
+
// to avoid a spike in traffic to the wbxappi service
|
|
813
|
+
let waitingTime = 0;
|
|
814
|
+
|
|
815
|
+
if (destination.meeting) {
|
|
816
|
+
const {startTime} = destination.meeting;
|
|
817
|
+
const startTimeDate = new Date(startTime);
|
|
818
|
+
const startTimeDatestamp = startTimeDate.getTime();
|
|
819
|
+
const timeToStart = startTimeDatestamp - Date.now();
|
|
820
|
+
const maxWaitingTime = Math.max(Math.min(timeToStart, MAX_RANDOM_DELAY_FOR_MEETING_INFO), 0);
|
|
821
|
+
|
|
822
|
+
waitingTime = Math.round(Math.random() * maxWaitingTime);
|
|
823
|
+
}
|
|
824
|
+
const isMeetingActive = !!destination.fullState?.active;
|
|
825
|
+
const {enableUnifiedMeetings} = this.config.experimental;
|
|
826
|
+
|
|
827
|
+
if (enableUnifiedMeetings && !isMeetingActive && useRandomDelayForInfo && waitingTime > 0) {
|
|
828
|
+
meeting.fetchMeetingInfoTimeoutId = setTimeout(() => meeting.fetchMeetingInfo({}), waitingTime);
|
|
829
|
+
meeting.parseMeetingInfo(undefined, destination);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
await meeting.fetchMeetingInfo({});
|
|
833
|
+
}
|
|
807
834
|
}
|
|
808
835
|
catch (err) {
|
|
809
836
|
if (!(err instanceof CaptchaError) && !(err instanceof PasswordError)) {
|
package/src/metrics/constants.js
CHANGED
|
@@ -13,6 +13,8 @@ const BEHAVIORAL_METRICS = {
|
|
|
13
13
|
CONNECTION_SUCCESS: 'js_sdk_connection_success',
|
|
14
14
|
CONNECTION_FAILURE: 'js_sdk_connection_failures',
|
|
15
15
|
MEETING_LEAVE_FAILURE: 'js_sdk_meeting_leave_failure',
|
|
16
|
+
MEETING_END_ALL_FAILURE: 'js_sdk_meeting_end_for_all_failure',
|
|
17
|
+
MEETING_END_ALL_INITIATED: 'js_sdk_meeting_end_for_all_initiated',
|
|
16
18
|
GET_USER_MEDIA_FAILURE: 'js_sdk_get_user_media_failures',
|
|
17
19
|
GET_DISPLAY_MEDIA_FAILURE: 'js_sdk_get_display_media_failures',
|
|
18
20
|
JOIN_WITH_MEDIA_FAILURE: 'js_sdk_join_with_media_failures',
|
|
@@ -3,6 +3,7 @@ import sinon from 'sinon';
|
|
|
3
3
|
import {cloneDeep} from 'lodash';
|
|
4
4
|
import {assert} from '@webex/test-helper-chai';
|
|
5
5
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
6
|
+
|
|
6
7
|
import Meetings from '@webex/plugin-meetings';
|
|
7
8
|
import LocusInfo from '@webex/plugin-meetings/src/locus-info';
|
|
8
9
|
import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
|
|
@@ -68,7 +69,10 @@ describe('plugin-meetings', () => {
|
|
|
68
69
|
}
|
|
69
70
|
},
|
|
70
71
|
shareControl: {},
|
|
71
|
-
transcribe: {}
|
|
72
|
+
transcribe: {},
|
|
73
|
+
meetingContainer: {
|
|
74
|
+
meetingContainerUrl: 'http://new-url.com'
|
|
75
|
+
}
|
|
72
76
|
};
|
|
73
77
|
});
|
|
74
78
|
|
|
@@ -248,6 +252,52 @@ describe('plugin-meetings', () => {
|
|
|
248
252
|
lastModified: 'TODAY'
|
|
249
253
|
});
|
|
250
254
|
});
|
|
255
|
+
|
|
256
|
+
it('should update the meetingContainerURL from null', () => {
|
|
257
|
+
locusInfo.controls = {
|
|
258
|
+
meetingContainer: {meetingContainerUrl: null},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
locusInfo.emitScoped = sinon.stub();
|
|
262
|
+
locusInfo.updateControls(newControls);
|
|
263
|
+
|
|
264
|
+
assert.calledWith(locusInfo.emitScoped, {
|
|
265
|
+
file: 'locus-info',
|
|
266
|
+
function: 'updateControls'
|
|
267
|
+
},
|
|
268
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_CONTAINER_UPDATED,
|
|
269
|
+
{meetingContainerUrl: 'http://new-url.com'});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should update the meetingContainerURL from not null', () => {
|
|
273
|
+
locusInfo.controls = {
|
|
274
|
+
meetingContainer: {meetingContainerUrl: 'http://old-url.com'},
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
locusInfo.emitScoped = sinon.stub();
|
|
278
|
+
locusInfo.updateControls(newControls);
|
|
279
|
+
|
|
280
|
+
assert.calledWith(locusInfo.emitScoped, {
|
|
281
|
+
file: 'locus-info',
|
|
282
|
+
function: 'updateControls'
|
|
283
|
+
},
|
|
284
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_CONTAINER_UPDATED,
|
|
285
|
+
{meetingContainerUrl: 'http://new-url.com'});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should update the meetingContainerURL from missing', () => {
|
|
289
|
+
locusInfo.controls = {};
|
|
290
|
+
|
|
291
|
+
locusInfo.emitScoped = sinon.stub();
|
|
292
|
+
locusInfo.updateControls(newControls);
|
|
293
|
+
|
|
294
|
+
assert.calledWith(locusInfo.emitScoped, {
|
|
295
|
+
file: 'locus-info',
|
|
296
|
+
function: 'updateControls'
|
|
297
|
+
},
|
|
298
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_CONTAINER_UPDATED,
|
|
299
|
+
{meetingContainerUrl: 'http://new-url.com'});
|
|
300
|
+
});
|
|
251
301
|
});
|
|
252
302
|
|
|
253
303
|
describe('#updateParticipants()', () => {
|