@webex/plugin-meetings 3.9.0 → 3.10.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/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +22 -5
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/locusRouteToken.js +116 -0
- package/dist/interceptors/locusRouteToken.js.map +1 -0
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +11 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +56 -14
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +4 -1
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +8 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +339 -185
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +2 -5
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +177 -14
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +39 -11
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -21
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +31 -25
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +42 -20
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js +7 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/index.js +3 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +7 -0
- package/dist/types/controls-options-manager/index.d.ts +9 -1
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/locusRouteToken.d.ts +38 -0
- package/dist/types/locus-info/index.d.ts +56 -2
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
- package/dist/types/meeting/index.d.ts +41 -1
- package/dist/types/meeting/request.d.ts +42 -0
- package/dist/types/meeting/util.d.ts +13 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +12 -2
- package/dist/types/members/util.d.ts +6 -3
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/constants.ts +10 -0
- package/src/controls-options-manager/index.ts +26 -5
- package/src/index.ts +2 -1
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/locusRouteToken.ts +80 -0
- package/src/locus-info/controlsUtils.ts +18 -0
- package/src/locus-info/index.ts +99 -17
- package/src/locus-info/parser.ts +5 -1
- package/src/media/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +16 -0
- package/src/meeting/index.ts +204 -24
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +141 -0
- package/src/meeting/util.ts +50 -20
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +9 -3
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +14 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +38 -5
- package/src/members/util.ts +18 -2
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/common/browser-detection.js +0 -24
- package/test/unit/spec/controls-options-manager/index.js +47 -0
- package/test/unit/spec/fixture/locus.js +1 -0
- package/test/unit/spec/interceptors/locusRouteToken.ts +87 -0
- package/test/unit/spec/locus-info/index.js +91 -15
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -0
- package/test/unit/spec/meeting/index.js +398 -30
- package/test/unit/spec/meeting/muteState.js +32 -6
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +49 -17
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +10 -5
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +72 -3
- package/test/unit/spec/members/request.js +55 -0
- package/test/unit/spec/members/utils.js +116 -14
- package/test/unit/spec/reachability/index.ts +158 -3
- package/test/unit/spec/roap/turnDiscovery.ts +3 -3
|
@@ -371,6 +371,7 @@ export default class MeetingInfoV2 {
|
|
|
371
371
|
* @param {String} conversationUrl conversationUrl to start adhoc meeting on
|
|
372
372
|
* @param {String} installedOrgID org ID of user's machine
|
|
373
373
|
* @param {Boolean} enableStaticMeetingLink whether or not to enable static meeting link
|
|
374
|
+
* @param {String} classificationId need it to start adhoc meeting if space support classification
|
|
374
375
|
* @returns {Promise} returns a meeting info object
|
|
375
376
|
* @public
|
|
376
377
|
* @memberof MeetingInfo
|
|
@@ -379,7 +380,8 @@ export default class MeetingInfoV2 {
|
|
|
379
380
|
conversationUrl: string,
|
|
380
381
|
installedOrgID?: string,
|
|
381
382
|
// setting this to true enables static meeting link
|
|
382
|
-
enableStaticMeetingLink = false
|
|
383
|
+
enableStaticMeetingLink = false,
|
|
384
|
+
classificationId = undefined
|
|
383
385
|
) {
|
|
384
386
|
const getInvitees = (particpants = []) => {
|
|
385
387
|
const invitees = [];
|
|
@@ -407,6 +409,7 @@ export default class MeetingInfoV2 {
|
|
|
407
409
|
invitees: getInvitees(conversation.participants?.items),
|
|
408
410
|
installedOrgID,
|
|
409
411
|
schedule: enableStaticMeetingLink,
|
|
412
|
+
classificationId,
|
|
410
413
|
};
|
|
411
414
|
|
|
412
415
|
if (installedOrgID) {
|
|
@@ -429,16 +432,26 @@ export default class MeetingInfoV2 {
|
|
|
429
432
|
* Creates adhoc space meetings for a space by fetching the conversation infomation
|
|
430
433
|
* @param {String} conversationUrl conversationUrl to start adhoc meeting on
|
|
431
434
|
* @param {String} installedOrgID org ID of user's machine
|
|
435
|
+
* @param {String} classificationId if space is support classification, it needs provide it during start instant meeting
|
|
432
436
|
* @returns {Promise} returns a meeting info object
|
|
433
437
|
* @public
|
|
434
438
|
* @memberof MeetingInfo
|
|
435
439
|
*/
|
|
436
|
-
async createAdhocSpaceMeeting(
|
|
440
|
+
async createAdhocSpaceMeeting(
|
|
441
|
+
conversationUrl: string,
|
|
442
|
+
installedOrgID?: string,
|
|
443
|
+
classificationId?: string
|
|
444
|
+
) {
|
|
437
445
|
if (!this.webex.meetings.preferredWebexSite) {
|
|
438
446
|
throw Error('No preferred webex site found');
|
|
439
447
|
}
|
|
440
448
|
|
|
441
|
-
return this.createAdhocSpaceMeetingOrEnableStaticMeetingLink(
|
|
449
|
+
return this.createAdhocSpaceMeetingOrEnableStaticMeetingLink(
|
|
450
|
+
conversationUrl,
|
|
451
|
+
installedOrgID,
|
|
452
|
+
false,
|
|
453
|
+
classificationId
|
|
454
|
+
)
|
|
442
455
|
.then((requestResult) => {
|
|
443
456
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
|
|
444
457
|
|
|
@@ -618,6 +631,7 @@ export default class MeetingInfoV2 {
|
|
|
618
631
|
* @param {Object} options
|
|
619
632
|
* @param {String} registrationId
|
|
620
633
|
* @param {String} fullSiteUrl
|
|
634
|
+
* @param {String} classificationId
|
|
621
635
|
* @returns {Promise} returns a meeting info object
|
|
622
636
|
* @public
|
|
623
637
|
* @memberof MeetingInfo
|
|
@@ -635,7 +649,8 @@ export default class MeetingInfoV2 {
|
|
|
635
649
|
extraParams: object = {},
|
|
636
650
|
options: {meetingId?: string; sendCAevents?: boolean} = {},
|
|
637
651
|
registrationId: string = null,
|
|
638
|
-
fullSiteUrl: string = null
|
|
652
|
+
fullSiteUrl: string = null,
|
|
653
|
+
classificationId: string = null
|
|
639
654
|
) {
|
|
640
655
|
const {meetingId, sendCAevents} = options;
|
|
641
656
|
|
|
@@ -650,7 +665,11 @@ export default class MeetingInfoV2 {
|
|
|
650
665
|
this.webex.config.meetings.experimental.enableAdhocMeetings &&
|
|
651
666
|
this.webex.meetings.preferredWebexSite
|
|
652
667
|
) {
|
|
653
|
-
return this.createAdhocSpaceMeeting(
|
|
668
|
+
return this.createAdhocSpaceMeeting(
|
|
669
|
+
destinationType.destination,
|
|
670
|
+
installedOrgID,
|
|
671
|
+
classificationId
|
|
672
|
+
);
|
|
654
673
|
}
|
|
655
674
|
|
|
656
675
|
const body = await MeetingInfoUtil.getRequestBody({
|
package/src/meetings/index.ts
CHANGED
|
@@ -1331,6 +1331,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
1331
1331
|
* @param {Object} [meetingInfo] - Pre-fetched complete meeting info
|
|
1332
1332
|
* @param {String} [meetingLookupUrl] - meeting info prefetch url
|
|
1333
1333
|
* @param {string} sessionCorrelationId - the optional specified sessionCorrelationId (callStateForMetrics.sessionCorrelationId) can be provided instead
|
|
1334
|
+
* @param {String} classificationId - If space support classification, it will provide it while start instant meeting
|
|
1334
1335
|
* @returns {Promise<Meeting>} A new Meeting.
|
|
1335
1336
|
* @public
|
|
1336
1337
|
* @memberof Meetings
|
|
@@ -1345,7 +1346,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1345
1346
|
callStateForMetrics: CallStateForMetrics = undefined,
|
|
1346
1347
|
meetingInfo = undefined,
|
|
1347
1348
|
meetingLookupUrl = undefined,
|
|
1348
|
-
sessionCorrelationId: string = undefined
|
|
1349
|
+
sessionCorrelationId: string = undefined,
|
|
1350
|
+
classificationId: string = undefined
|
|
1349
1351
|
) {
|
|
1350
1352
|
// Validate meeting information based on the provided destination and
|
|
1351
1353
|
// type. This must be performed prior to determining if the meeting is
|
|
@@ -1415,7 +1417,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1415
1417
|
callStateForMetrics,
|
|
1416
1418
|
failOnMissingMeetingInfo,
|
|
1417
1419
|
meetingInfo,
|
|
1418
|
-
meetingLookupUrl
|
|
1420
|
+
meetingLookupUrl,
|
|
1421
|
+
classificationId
|
|
1419
1422
|
).then((createdMeeting: any) => {
|
|
1420
1423
|
// If the meeting was successfully created.
|
|
1421
1424
|
if (createdMeeting && createdMeeting.on) {
|
|
@@ -1529,6 +1532,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
1529
1532
|
* @param {Boolean} failOnMissingMeetingInfo - whether to throw an error if meeting info fails to fetch (for calls that are not 1:1 or content share)
|
|
1530
1533
|
* @param {Object} [meetingInfo] - Pre-fetched complete meeting info
|
|
1531
1534
|
* @param {String} [meetingLookupUrl] - meeting info prefetch url
|
|
1535
|
+
* @param {String} classificationId see create()
|
|
1532
1536
|
* @returns {Promise} a new meeting instance complete with meeting info and destination
|
|
1533
1537
|
* @private
|
|
1534
1538
|
* @memberof Meetings
|
|
@@ -1541,7 +1545,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1541
1545
|
callStateForMetrics: CallStateForMetrics = undefined,
|
|
1542
1546
|
failOnMissingMeetingInfo = false,
|
|
1543
1547
|
meetingInfo = undefined,
|
|
1544
|
-
meetingLookupUrl = undefined
|
|
1548
|
+
meetingLookupUrl = undefined,
|
|
1549
|
+
classificationId = undefined
|
|
1545
1550
|
) {
|
|
1546
1551
|
const meeting = new Meeting(
|
|
1547
1552
|
{
|
|
@@ -1589,6 +1594,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
1589
1594
|
// @ts-ignore
|
|
1590
1595
|
const {enableUnifiedMeetings} = this.config.experimental;
|
|
1591
1596
|
const meetingInfoOptions = {
|
|
1597
|
+
classificationId,
|
|
1592
1598
|
extraParams: infoExtraParams,
|
|
1593
1599
|
sendCAevents: !!callStateForMetrics?.correlationId, // if client sends correlation id as argument of public create(), then it means that this meeting creation is part of a pre-join intent from user
|
|
1594
1600
|
};
|
package/src/member/index.ts
CHANGED
|
@@ -42,6 +42,7 @@ export default class Member {
|
|
|
42
42
|
status: any;
|
|
43
43
|
supportsBreakouts: boolean;
|
|
44
44
|
supportsInterpretation: boolean;
|
|
45
|
+
supportsSingleUserAutoEndMeeting: boolean;
|
|
45
46
|
supportLiveAnnotation: boolean;
|
|
46
47
|
type: any;
|
|
47
48
|
namespace = MEETINGS;
|
|
@@ -130,6 +131,13 @@ export default class Member {
|
|
|
130
131
|
* @memberof Member
|
|
131
132
|
*/
|
|
132
133
|
this.supportsBreakouts = null;
|
|
134
|
+
/**
|
|
135
|
+
* @instance
|
|
136
|
+
* @type {Boolean}
|
|
137
|
+
* @public
|
|
138
|
+
* @memberof Member
|
|
139
|
+
*/
|
|
140
|
+
this.supportsSingleUserAutoEndMeeting = null;
|
|
133
141
|
/**
|
|
134
142
|
* @instance
|
|
135
143
|
* @type {Boolean}
|
|
@@ -339,6 +347,8 @@ export default class Member {
|
|
|
339
347
|
this.isVideoMuted = MemberUtil.isVideoMuted(participant);
|
|
340
348
|
this.isHandRaised = MemberUtil.isHandRaised(participant);
|
|
341
349
|
this.supportsBreakouts = MemberUtil.isBreakoutsSupported(participant);
|
|
350
|
+
this.supportsSingleUserAutoEndMeeting =
|
|
351
|
+
MemberUtil.isSupportsSingleUserAutoEndMeeting(participant);
|
|
342
352
|
this.supportsInterpretation = MemberUtil.isInterpretationSupported(participant);
|
|
343
353
|
this.supportLiveAnnotation = MemberUtil.isLiveAnnotationSupported(participant);
|
|
344
354
|
this.isGuest = MemberUtil.isGuest(participant);
|
package/src/member/types.ts
CHANGED
package/src/member/util.ts
CHANGED
|
@@ -207,6 +207,20 @@ const MemberUtil = {
|
|
|
207
207
|
return !participant.doesNotSupportBreakouts;
|
|
208
208
|
},
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* @param {Object} participant - The locus participant object.
|
|
212
|
+
* @returns {Boolean}
|
|
213
|
+
*/
|
|
214
|
+
isSupportsSingleUserAutoEndMeeting: (participant) => {
|
|
215
|
+
if (!participant) {
|
|
216
|
+
throw new ParameterError(
|
|
217
|
+
'Single user auto end meeting support could not be processed, participant is undefined.'
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return !participant.doesNotSupportSingleUserAutoEndMeeting;
|
|
222
|
+
},
|
|
223
|
+
|
|
210
224
|
/**
|
|
211
225
|
* @param {Object} participant - The locus participant object.
|
|
212
226
|
* @returns {Boolean}
|
|
@@ -39,6 +39,17 @@ export default class MembersCollection {
|
|
|
39
39
|
return this.members;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Removes a member from the collection
|
|
44
|
+
* @param {String} id
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
remove(id: string) {
|
|
48
|
+
if (this.members[id]) {
|
|
49
|
+
delete this.members[id];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
42
53
|
/**
|
|
43
54
|
* @returns {void}
|
|
44
55
|
* reset members
|
package/src/members/index.ts
CHANGED
|
@@ -74,7 +74,11 @@ import {Invitee} from '../meeting/type';
|
|
|
74
74
|
* @memberof Members
|
|
75
75
|
*/
|
|
76
76
|
|
|
77
|
-
type UpdatedMembers = {
|
|
77
|
+
type UpdatedMembers = {
|
|
78
|
+
added: Array<Member>;
|
|
79
|
+
updated: Array<Member>;
|
|
80
|
+
removedIds?: Array<string>; // removed member ids
|
|
81
|
+
};
|
|
78
82
|
/**
|
|
79
83
|
* @class Members
|
|
80
84
|
*/
|
|
@@ -384,12 +388,18 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
384
388
|
* when new participant updates come in, both delta and full participants, update them in members collection
|
|
385
389
|
* delta object in the event will have {updated, added} and full will be the full membersCollection
|
|
386
390
|
* @param {Object} payload
|
|
387
|
-
* @param {Object} payload.participants
|
|
391
|
+
* @param {Object} payload.participants new/updated participants
|
|
392
|
+
* @param {Boolean} payload.isReplace whether to replace the whole members collection
|
|
393
|
+
* @param {Object} payload.removedParticipantIds ids of the removed participants
|
|
388
394
|
* @returns {undefined}
|
|
389
395
|
* @private
|
|
390
396
|
* @memberof Members
|
|
391
397
|
*/
|
|
392
|
-
locusParticipantsUpdate(payload: {
|
|
398
|
+
locusParticipantsUpdate(payload: {
|
|
399
|
+
participants: object;
|
|
400
|
+
isReplace?: boolean;
|
|
401
|
+
removedParticipantIds?: Array<string>;
|
|
402
|
+
}) {
|
|
393
403
|
if (payload) {
|
|
394
404
|
if (payload.isReplace) {
|
|
395
405
|
this.clearMembers();
|
|
@@ -547,10 +557,22 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
547
557
|
private handleMembersUpdate(membersUpdate: UpdatedMembers) {
|
|
548
558
|
this.constructMembers(membersUpdate.updated, true);
|
|
549
559
|
this.constructMembers(membersUpdate.added, false);
|
|
560
|
+
this.removeMembers(membersUpdate.removedIds);
|
|
550
561
|
|
|
551
562
|
return this.membersCollection.getAll();
|
|
552
563
|
}
|
|
553
564
|
|
|
565
|
+
/**
|
|
566
|
+
* removes members from the collection
|
|
567
|
+
* @param {Array<string>} removedMembers removed members ids
|
|
568
|
+
* @returns {void}
|
|
569
|
+
*/
|
|
570
|
+
private removeMembers(removedMembers: Array<string>) {
|
|
571
|
+
removedMembers.forEach((memberId) => {
|
|
572
|
+
this.membersCollection.remove(memberId);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
554
576
|
/**
|
|
555
577
|
* set members to the member collection from each updated/added lists as passed in
|
|
556
578
|
* @param {Array} list
|
|
@@ -600,6 +622,10 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
600
622
|
}
|
|
601
623
|
const memberUpdate = this.update(payload.participants);
|
|
602
624
|
|
|
625
|
+
// this code depends on memberIds being the same as participantIds
|
|
626
|
+
// if MemberUtil.extractId() ever changes, this will need to be updated
|
|
627
|
+
memberUpdate.removedIds = payload.removedParticipantIds || [];
|
|
628
|
+
|
|
603
629
|
return memberUpdate;
|
|
604
630
|
}
|
|
605
631
|
|
|
@@ -1181,11 +1207,17 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
1181
1207
|
* @param {string} memberId - id of the participant who is receiving request
|
|
1182
1208
|
* @param {string} requestingParticipantId - id of the participant who is sending request (optional)
|
|
1183
1209
|
* @param {string} [alias] - alias name
|
|
1210
|
+
* @param {string} [suffix] - name suffix (optional)
|
|
1184
1211
|
* @returns {Promise}
|
|
1185
1212
|
* @public
|
|
1186
1213
|
* @memberof Members
|
|
1187
1214
|
*/
|
|
1188
|
-
public editDisplayName(
|
|
1215
|
+
public editDisplayName(
|
|
1216
|
+
memberId: string,
|
|
1217
|
+
requestingParticipantId: string,
|
|
1218
|
+
alias: string,
|
|
1219
|
+
suffix?: string
|
|
1220
|
+
) {
|
|
1189
1221
|
if (!this.locusUrl) {
|
|
1190
1222
|
return Promise.reject(
|
|
1191
1223
|
new ParameterError(
|
|
@@ -1205,7 +1237,8 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
1205
1237
|
memberId,
|
|
1206
1238
|
requestingParticipantId,
|
|
1207
1239
|
alias,
|
|
1208
|
-
locusUrl
|
|
1240
|
+
locusUrl,
|
|
1241
|
+
suffix
|
|
1209
1242
|
);
|
|
1210
1243
|
|
|
1211
1244
|
return this.membersRequest.editDisplayNameMember(options);
|
package/src/members/util.ts
CHANGED
|
@@ -190,13 +190,21 @@ const MembersUtil = {
|
|
|
190
190
|
* @param {String} requestingParticipantId id of the participant who is sending request (optional)
|
|
191
191
|
* @param {String} alias alias name
|
|
192
192
|
* @param {String} locusUrl url
|
|
193
|
+
* @param {String} suffix optional suffix
|
|
193
194
|
* @returns {Object} consists of {memberID: string, requestingParticipantId: string, alias: string, locusUrl: string}
|
|
194
195
|
*/
|
|
195
|
-
generateEditDisplayNameMemberOptions: (
|
|
196
|
+
generateEditDisplayNameMemberOptions: (
|
|
196
197
|
memberId,
|
|
197
198
|
requestingParticipantId,
|
|
198
199
|
alias,
|
|
199
200
|
locusUrl,
|
|
201
|
+
suffix
|
|
202
|
+
) => ({
|
|
203
|
+
memberId,
|
|
204
|
+
requestingParticipantId,
|
|
205
|
+
alias,
|
|
206
|
+
locusUrl,
|
|
207
|
+
suffix,
|
|
200
208
|
}),
|
|
201
209
|
|
|
202
210
|
getMuteMemberRequestParams: (options) => {
|
|
@@ -301,10 +309,18 @@ const MembersUtil = {
|
|
|
301
309
|
* @returns {Object} request parameters (method, uri, body) needed to make a editDisplayName request
|
|
302
310
|
*/
|
|
303
311
|
editDisplayNameMemberRequestParams: (options) => {
|
|
304
|
-
const body
|
|
312
|
+
const body: {
|
|
313
|
+
aliasValue: string;
|
|
314
|
+
requestingParticipantId: string;
|
|
315
|
+
suffixValue?: string;
|
|
316
|
+
} = {
|
|
305
317
|
aliasValue: options.alias,
|
|
306
318
|
requestingParticipantId: options.requestingParticipantId,
|
|
307
319
|
};
|
|
320
|
+
|
|
321
|
+
if (options.suffix !== undefined) {
|
|
322
|
+
body.suffixValue = options.suffix;
|
|
323
|
+
}
|
|
308
324
|
const uri = `${options.locusUrl}/${PARTICIPANT}/${options.memberId}/${ALIAS}`;
|
|
309
325
|
|
|
310
326
|
return {
|
package/src/metrics/constants.ts
CHANGED
|
@@ -86,6 +86,7 @@ const BEHAVIORAL_METRICS = {
|
|
|
86
86
|
VERIFY_REGISTRATION_ID_SUCCESS: 'js_sdk_verify_registrationId_success',
|
|
87
87
|
VERIFY_REGISTRATION_ID_ERROR: 'js_sdk_verify_registrationId_error',
|
|
88
88
|
JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
|
|
89
|
+
MEDIA_ISSUE_DETECTED: 'js_sdk_media_issue_detected',
|
|
89
90
|
};
|
|
90
91
|
|
|
91
92
|
export {BEHAVIORAL_METRICS as default};
|
|
@@ -923,10 +923,10 @@ export default class Reachability extends EventsScope {
|
|
|
923
923
|
|
|
924
924
|
// update expected results counters to include this cluster
|
|
925
925
|
this.expectedResultsCount[cluster.isVideoMesh ? 'videoMesh' : 'public'].udp +=
|
|
926
|
-
cluster.udp.length;
|
|
926
|
+
cluster.udp.length > 0 ? 1 : 0;
|
|
927
927
|
if (!cluster.isVideoMesh) {
|
|
928
|
-
this.expectedResultsCount.public.tcp += cluster.tcp.length;
|
|
929
|
-
this.expectedResultsCount.public.xtls += cluster.xtls.length;
|
|
928
|
+
this.expectedResultsCount.public.tcp += cluster.tcp.length > 0 ? 1 : 0;
|
|
929
|
+
this.expectedResultsCount.public.xtls += cluster.xtls.length > 0 ? 1 : 0;
|
|
930
930
|
}
|
|
931
931
|
});
|
|
932
932
|
|
|
@@ -18,16 +18,6 @@ const USER_AGENT_SAFARI_MAC =
|
|
|
18
18
|
const USER_AGENT_FIREFOX_MAC =
|
|
19
19
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) ' + 'Gecko/20100101 Firefox/87.0';
|
|
20
20
|
|
|
21
|
-
const mockDetectionObject = {
|
|
22
|
-
/* eslint-disable global-require */
|
|
23
|
-
getOSName: () => require('os').platform(),
|
|
24
|
-
getOSVersion: () => require('os').release(),
|
|
25
|
-
/* eslint-enable global-require */
|
|
26
|
-
getBrowserName: () => '',
|
|
27
|
-
getBrowserVersion: () => '',
|
|
28
|
-
isBrowser: () => false,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
21
|
describe('common/browser-detection', () => {
|
|
32
22
|
it('returns the correct browser name.', () => {
|
|
33
23
|
assert.equal(
|
|
@@ -102,18 +92,4 @@ describe('common/browser-detection', () => {
|
|
|
102
92
|
'This browser is NOT Firefox'
|
|
103
93
|
);
|
|
104
94
|
});
|
|
105
|
-
|
|
106
|
-
it('returns the mock object when there is no userAgent', () => {
|
|
107
|
-
Object.defineProperty(global.window.navigator, 'userAgent', {
|
|
108
|
-
get: () => undefined,
|
|
109
|
-
configurable: true,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const {getBrowserName, getBrowserVersion, getOSName, getOSVersion} = BrowserDetection(null);
|
|
113
|
-
|
|
114
|
-
assert.equal(getBrowserName(), mockDetectionObject.getBrowserName());
|
|
115
|
-
assert.equal(getBrowserVersion(), mockDetectionObject.getBrowserVersion());
|
|
116
|
-
assert.equal(getOSName(), mockDetectionObject.getOSName());
|
|
117
|
-
assert.equal(getOSVersion(), mockDetectionObject.getOSVersion());
|
|
118
|
-
});
|
|
119
95
|
});
|
|
@@ -133,6 +133,7 @@ describe('plugin-meetings', () => {
|
|
|
133
133
|
|
|
134
134
|
manager.set({
|
|
135
135
|
locusUrl: 'test/id',
|
|
136
|
+
mainLocusUrl: '',
|
|
136
137
|
displayHints: [],
|
|
137
138
|
});
|
|
138
139
|
});
|
|
@@ -201,6 +202,38 @@ describe('plugin-meetings', () => {
|
|
|
201
202
|
Util.canUpdate = restorable;
|
|
202
203
|
});
|
|
203
204
|
});
|
|
205
|
+
|
|
206
|
+
it('should call request with mainLocusUrl and locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
|
|
207
|
+
const restorable = Util.canUpdate;
|
|
208
|
+
Util.canUpdate = sinon.stub().returns(true);
|
|
209
|
+
manager.mainLocusUrl = 'test/main';
|
|
210
|
+
|
|
211
|
+
const audio = {scope: 'audio', properties: {a: 1, b: 2}};
|
|
212
|
+
const reactions = {scope: 'reactions', properties: {c: 3, d: 4}};
|
|
213
|
+
|
|
214
|
+
return manager.update(audio, reactions)
|
|
215
|
+
.then(() => {
|
|
216
|
+
assert.calledWith(request.request, {
|
|
217
|
+
uri: 'test/main/controls',
|
|
218
|
+
body: {
|
|
219
|
+
audio: audio.properties,
|
|
220
|
+
authorizingLocusUrl: 'test/id'
|
|
221
|
+
},
|
|
222
|
+
method: HTTP_VERBS.PATCH,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
assert.calledWith(request.request, {
|
|
226
|
+
uri: 'test/main/controls',
|
|
227
|
+
body: {
|
|
228
|
+
reactions: reactions.properties,
|
|
229
|
+
authorizingLocusUrl: 'test/id'
|
|
230
|
+
},
|
|
231
|
+
method: HTTP_VERBS.PATCH,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
Util.canUpdate = restorable;
|
|
235
|
+
});
|
|
236
|
+
});
|
|
204
237
|
});
|
|
205
238
|
|
|
206
239
|
describe('Mute/Unmute All', () => {
|
|
@@ -214,6 +247,7 @@ describe('plugin-meetings', () => {
|
|
|
214
247
|
|
|
215
248
|
manager.set({
|
|
216
249
|
locusUrl: 'test/id',
|
|
250
|
+
mainLocusUrl: '',
|
|
217
251
|
displayHints: [],
|
|
218
252
|
})
|
|
219
253
|
});
|
|
@@ -305,6 +339,19 @@ describe('plugin-meetings', () => {
|
|
|
305
339
|
|
|
306
340
|
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
307
341
|
});
|
|
342
|
+
|
|
343
|
+
it('request with mainLocusUrl and make locusUrl as authorizingLocusUrl if mainLocusUrl is exist and not same with locusUrl', () => {
|
|
344
|
+
manager.setDisplayHints(['MUTE_ALL', 'DISABLE_HARD_MUTE', 'DISABLE_MUTE_ON_ENTRY']);
|
|
345
|
+
manager.mainLocusUrl = `test/main`;
|
|
346
|
+
|
|
347
|
+
const result = manager.setMuteAll(true, true, true, ['attendee']);
|
|
348
|
+
|
|
349
|
+
assert.calledWith(request.request, { uri: 'test/main/controls',
|
|
350
|
+
body: { audio: { muted: true, disallowUnmute: true, muteOnEntry: true, roles: ['attendee'] }, authorizingLocusUrl: 'test/id' },
|
|
351
|
+
method: HTTP_VERBS.PATCH});
|
|
352
|
+
|
|
353
|
+
assert.deepEqual(result, request.request.firstCall.returnValue);
|
|
354
|
+
});
|
|
308
355
|
});
|
|
309
356
|
});
|
|
310
357
|
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* eslint-disable camelcase */
|
|
6
|
+
import 'jsdom-global/register';
|
|
7
|
+
import {assert} from '@webex/test-helper-chai';
|
|
8
|
+
import MockWebex from '@webex/test-helper-mock-webex';
|
|
9
|
+
import {LocusRouteTokenInterceptor} from '@webex/plugin-meetings/src/interceptors';
|
|
10
|
+
import Meetings from '@webex/plugin-meetings';
|
|
11
|
+
|
|
12
|
+
const X_CISCO_PART_ROUTE_TOKEN = 'X-Cisco-Part-Route-Token';
|
|
13
|
+
|
|
14
|
+
describe('LocusRouteTokenInterceptor', () => {
|
|
15
|
+
let interceptor, webex;
|
|
16
|
+
const TEST_LOCUS_ID = '0f1eba56-91e2-2a11-9b2b-1e2da077f066';
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
webex = new MockWebex({
|
|
19
|
+
children: {
|
|
20
|
+
meetings: Meetings,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
interceptor = Reflect.apply(LocusRouteTokenInterceptor.create, webex, []);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('getLocusIdByRequestUrl should return locusId from url', () => {
|
|
27
|
+
const url = `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`;
|
|
28
|
+
assert.equal(interceptor.getLocusIdByRequestUrl(url), TEST_LOCUS_ID);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('getLocusIdByRequestUrl should return undefined when no locusId in url', () => {
|
|
32
|
+
const url = 'https://locus-test.webex.com/locus/api/v1/foo';
|
|
33
|
+
assert.isUndefined(interceptor.getLocusIdByRequestUrl(url));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('getLocusIdByRequestUrl should return undefined when url is undefined', () => {
|
|
37
|
+
assert.isUndefined(interceptor.getLocusIdByRequestUrl(undefined));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('onResponse should store route token when header exists', async () => {
|
|
41
|
+
const response = {
|
|
42
|
+
headers: {
|
|
43
|
+
[X_CISCO_PART_ROUTE_TOKEN]: 'test-token',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const result = await interceptor.onResponse(
|
|
48
|
+
{
|
|
49
|
+
uri: `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`,
|
|
50
|
+
},
|
|
51
|
+
response
|
|
52
|
+
);
|
|
53
|
+
assert.equal(result, response);
|
|
54
|
+
assert.equal(interceptor.getToken(TEST_LOCUS_ID), 'test-token');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('onResponse should not store token when header missing', async () => {
|
|
58
|
+
interceptor.updateToken(TEST_LOCUS_ID);
|
|
59
|
+
const response = {headers: {}};
|
|
60
|
+
|
|
61
|
+
await interceptor.onResponse({}, response);
|
|
62
|
+
assert.isUndefined(interceptor.getToken(TEST_LOCUS_ID));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('onRequest should attach token to headers when token exists', async () => {
|
|
66
|
+
interceptor.updateToken(TEST_LOCUS_ID, 'abc123');
|
|
67
|
+
|
|
68
|
+
const options = {
|
|
69
|
+
headers: {},
|
|
70
|
+
uri: `https://locus-test.webex.com/locus/api/v1/loci/${TEST_LOCUS_ID}/foo`,
|
|
71
|
+
};
|
|
72
|
+
const result = await interceptor.onRequest(options);
|
|
73
|
+
assert.equal(result.headers[X_CISCO_PART_ROUTE_TOKEN], 'abc123');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('onRequest should not attach token if none is stored', async () => {
|
|
77
|
+
interceptor.updateToken(TEST_LOCUS_ID);
|
|
78
|
+
const options = {headers: {}};
|
|
79
|
+
const result = await interceptor.onRequest(options);
|
|
80
|
+
assert.isUndefined(result.headers[X_CISCO_PART_ROUTE_TOKEN]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('updateToken & getToken should work as pair', () => {
|
|
84
|
+
interceptor.updateToken(TEST_LOCUS_ID, 'abc456');
|
|
85
|
+
assert.equal(interceptor.getToken(TEST_LOCUS_ID), 'abc456');
|
|
86
|
+
});
|
|
87
|
+
});
|