@webex/plugin-meetings 3.8.1-next.31 → 3.8.1-next.33
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 +15 -6
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/type.js +7 -0
- package/dist/meeting/type.js.map +1 -0
- package/dist/members/index.js +4 -4
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js.map +1 -1
- package/dist/types/meeting/index.d.ts +9 -16
- package/dist/types/meeting/type.d.ts +9 -0
- package/dist/types/members/index.d.ts +7 -9
- package/dist/types/members/util.d.ts +2 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +3 -3
- package/src/meeting/index.ts +19 -20
- package/src/meeting/type.ts +9 -0
- package/src/members/index.ts +8 -7
- package/src/members/util.ts +2 -1
- package/test/unit/spec/meeting/index.js +34 -5
- package/test/unit/spec/members/index.js +36 -0
@@ -4,6 +4,7 @@ import MembersCollection from './collection';
|
|
4
4
|
import { ReceiveSlotManager } from '../multistream/receiveSlotManager';
|
5
5
|
import { MediaRequestManager } from '../multistream/mediaRequestManager';
|
6
6
|
import { ServerRoleShape } from './types';
|
7
|
+
import { Invitee } from '../meeting/type';
|
7
8
|
/**
|
8
9
|
* @class Members
|
9
10
|
*/
|
@@ -212,31 +213,28 @@ export default class Members extends StatelessWebexPlugin {
|
|
212
213
|
private update;
|
213
214
|
/**
|
214
215
|
* Adds a guest Member to the associated meeting
|
215
|
-
* @param {
|
216
|
+
* @param {Invitee} invitee
|
216
217
|
* @param {Boolean} [alertIfActive]
|
217
218
|
* @returns {Promise}
|
218
219
|
* @memberof Members
|
219
220
|
*/
|
220
|
-
addMember(invitee:
|
221
|
+
addMember(invitee: Invitee, alertIfActive?: boolean): any;
|
221
222
|
/**
|
222
223
|
* Cancels an outgoing PSTN call to the associated meeting
|
223
|
-
* @param {
|
224
|
+
* @param {Invitee} invitee
|
224
225
|
* @returns {Promise}
|
225
226
|
* @memberof Members
|
226
227
|
*/
|
227
|
-
cancelPhoneInvite(invitee:
|
228
|
+
cancelPhoneInvite(invitee: Invitee): any;
|
228
229
|
/**
|
229
230
|
* Cancels an SIP/phone call to the associated meeting
|
230
|
-
* @param {
|
231
|
+
* @param {Invitee} invitee
|
231
232
|
* @param {String} invitee.memberId - The memberId of the invitee
|
232
233
|
* @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
|
233
234
|
* @returns {Promise}
|
234
235
|
* @memberof Members
|
235
236
|
*/
|
236
|
-
cancelInviteByMemberId(invitee:
|
237
|
-
memberId: string;
|
238
|
-
isInternalNumber?: boolean;
|
239
|
-
}): any;
|
237
|
+
cancelInviteByMemberId(invitee: Invitee): any;
|
240
238
|
/**
|
241
239
|
* Admits waiting members (invited guests to meeting)
|
242
240
|
* @param {Array} memberIds
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { RoleAssignmentOptions, RoleAssignmentRequest, ServerRoleShape } from './types';
|
2
|
+
import { Invitee } from '../meeting/type';
|
2
3
|
declare const MembersUtil: {
|
3
4
|
/**
|
4
5
|
* @param {Object} invitee with emailAddress, email or phoneNumber
|
@@ -63,7 +64,7 @@ declare const MembersUtil: {
|
|
63
64
|
alertIfActive: any;
|
64
65
|
};
|
65
66
|
};
|
66
|
-
isInvalidInvitee: (invitee:
|
67
|
+
isInvalidInvitee: (invitee: Invitee) => boolean;
|
67
68
|
getRemoveMemberRequestParams: (options: any) => {
|
68
69
|
method: string;
|
69
70
|
uri: string;
|
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.1-next.
|
46
|
+
"@webex/plugin-meetings": "3.8.1-next.33",
|
47
47
|
"@webex/plugin-rooms": "3.8.1-next.7",
|
48
48
|
"@webex/test-helper-chai": "3.8.1-next.10",
|
49
49
|
"@webex/test-helper-mocha": "3.8.1-next.10",
|
@@ -71,7 +71,7 @@
|
|
71
71
|
"@webex/internal-plugin-metrics": "3.8.1-next.10",
|
72
72
|
"@webex/internal-plugin-support": "3.8.1-next.10",
|
73
73
|
"@webex/internal-plugin-user": "3.8.1-next.10",
|
74
|
-
"@webex/internal-plugin-voicea": "3.8.1-next.
|
74
|
+
"@webex/internal-plugin-voicea": "3.8.1-next.33",
|
75
75
|
"@webex/media-helpers": "3.8.1-next.14",
|
76
76
|
"@webex/plugin-people": "3.8.1-next.10",
|
77
77
|
"@webex/plugin-rooms": "3.8.1-next.7",
|
@@ -93,5 +93,5 @@
|
|
93
93
|
"//": [
|
94
94
|
"TODO: upgrade jwt-decode when moving to node 18"
|
95
95
|
],
|
96
|
-
"version": "3.8.1-next.
|
96
|
+
"version": "3.8.1-next.33"
|
97
97
|
}
|
package/src/meeting/index.ts
CHANGED
@@ -166,6 +166,7 @@ import MultistreamNotSupportedError from '../common/errors/multistream-not-suppo
|
|
166
166
|
import JoinForbiddenError from '../common/errors/join-forbidden-error';
|
167
167
|
import {ReachabilityMetrics} from '../reachability/reachability.types';
|
168
168
|
import {SetStageOptions, SetStageVideoLayout, UnsetStageVideoLayout} from './request.type';
|
169
|
+
import {Invitee} from './type';
|
169
170
|
|
170
171
|
// default callback so we don't call an undefined function, but in practice it should never be used
|
171
172
|
const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
|
@@ -3064,12 +3065,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
3064
3065
|
// There is no concept of local/remote share for whiteboard
|
3065
3066
|
// It does not matter who requested to share the whiteboard, everyone gets the same view
|
3066
3067
|
else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
|
3067
|
-
|
3068
|
-
|
3069
|
-
|
3070
|
-
|
3071
|
-
|
3072
|
-
|
3068
|
+
if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
|
3069
|
+
// WHITEBOARD - sharing whiteboard
|
3070
|
+
// Webinar attendee should receive whiteboard as remote share
|
3071
|
+
newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
|
3072
|
+
} else if (this.guest) {
|
3073
|
+
// If user is a guest to a meeting, they should receive whiteboard as remote share
|
3074
|
+
newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
|
3075
|
+
} else {
|
3076
|
+
newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
3077
|
+
}
|
3073
3078
|
}
|
3074
3079
|
// or if content share is either released or null and whiteboard share is either released or null, no one is sharing
|
3075
3080
|
else if (
|
@@ -3835,49 +3840,43 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
3835
3840
|
|
3836
3841
|
/**
|
3837
3842
|
* Invite a guest to the call that isn't normally part of this call
|
3838
|
-
* @param {
|
3843
|
+
* @param {Invitee} invitee
|
3839
3844
|
* @param {String} invitee.emailAddress
|
3840
3845
|
* @param {String} invitee.email
|
3841
3846
|
* @param {String} invitee.phoneNumber
|
3842
3847
|
* @param {Boolean} [alertIfActive]
|
3848
|
+
* @param {Boolean} [invitee.skipEmailValidation]
|
3849
|
+
* @param {Boolean} [invitee.isInternalNumber]
|
3843
3850
|
* @returns {Promise} see #members.addMember
|
3844
3851
|
* @public
|
3845
3852
|
* @memberof Meeting
|
3846
3853
|
*/
|
3847
|
-
public invite(
|
3848
|
-
invitee: {
|
3849
|
-
emailAddress: string;
|
3850
|
-
email: string;
|
3851
|
-
phoneNumber: string;
|
3852
|
-
roles: Array<string>;
|
3853
|
-
},
|
3854
|
-
alertIfActive = true
|
3855
|
-
) {
|
3854
|
+
public invite(invitee: Invitee, alertIfActive = true) {
|
3856
3855
|
return this.members.addMember(invitee, alertIfActive);
|
3857
3856
|
}
|
3858
3857
|
|
3859
3858
|
/**
|
3860
3859
|
* Cancel an outgoing phone call invitation made during a meeting
|
3861
|
-
* @param {
|
3860
|
+
* @param {Invitee} invitee
|
3862
3861
|
* @param {String} invitee.phoneNumber
|
3863
3862
|
* @returns {Promise} see #members.cancelPhoneInvite
|
3864
3863
|
* @public
|
3865
3864
|
* @memberof Meeting
|
3866
3865
|
*/
|
3867
|
-
public cancelPhoneInvite(invitee:
|
3866
|
+
public cancelPhoneInvite(invitee: Invitee) {
|
3868
3867
|
return this.members.cancelPhoneInvite(invitee);
|
3869
3868
|
}
|
3870
3869
|
|
3871
3870
|
/**
|
3872
3871
|
* Cancel an SIP/phone call invitation made during a meeting
|
3873
|
-
* @param {
|
3872
|
+
* @param {Invitee} invitee
|
3874
3873
|
* @param {String} invitee.memberId
|
3875
3874
|
* @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
|
3876
3875
|
* @returns {Promise} see #members.cancelInviteByMemberId
|
3877
3876
|
* @public
|
3878
3877
|
* @memberof Meeting
|
3879
3878
|
*/
|
3880
|
-
public cancelInviteByMemberId(invitee:
|
3879
|
+
public cancelInviteByMemberId(invitee: Invitee) {
|
3881
3880
|
return this.members.cancelInviteByMemberId(invitee);
|
3882
3881
|
}
|
3883
3882
|
|
package/src/members/index.ts
CHANGED
@@ -30,6 +30,7 @@ import MembersUtil from './util';
|
|
30
30
|
import {ReceiveSlotManager} from '../multistream/receiveSlotManager';
|
31
31
|
import {MediaRequestManager} from '../multistream/mediaRequestManager';
|
32
32
|
import {ServerRoleShape} from './types';
|
33
|
+
import {Invitee} from '../meeting/type';
|
33
34
|
|
34
35
|
/**
|
35
36
|
* Members Update Event
|
@@ -800,18 +801,18 @@ export default class Members extends StatelessWebexPlugin {
|
|
800
801
|
|
801
802
|
/**
|
802
803
|
* Adds a guest Member to the associated meeting
|
803
|
-
* @param {
|
804
|
+
* @param {Invitee} invitee
|
804
805
|
* @param {Boolean} [alertIfActive]
|
805
806
|
* @returns {Promise}
|
806
807
|
* @memberof Members
|
807
808
|
*/
|
808
|
-
addMember(invitee:
|
809
|
+
addMember(invitee: Invitee, alertIfActive?: boolean) {
|
809
810
|
if (!this.locusUrl) {
|
810
811
|
return Promise.reject(
|
811
812
|
new ParameterError('The associated locus url for this meeting object must be defined.')
|
812
813
|
);
|
813
814
|
}
|
814
|
-
if (MembersUtil.isInvalidInvitee(invitee)) {
|
815
|
+
if (invitee?.skipEmailValidation !== true && MembersUtil.isInvalidInvitee(invitee)) {
|
815
816
|
return Promise.reject(
|
816
817
|
new ParameterError(
|
817
818
|
'The invitee must be defined with either a valid email, emailAddress or phoneNumber property.'
|
@@ -825,11 +826,11 @@ export default class Members extends StatelessWebexPlugin {
|
|
825
826
|
|
826
827
|
/**
|
827
828
|
* Cancels an outgoing PSTN call to the associated meeting
|
828
|
-
* @param {
|
829
|
+
* @param {Invitee} invitee
|
829
830
|
* @returns {Promise}
|
830
831
|
* @memberof Members
|
831
832
|
*/
|
832
|
-
cancelPhoneInvite(invitee:
|
833
|
+
cancelPhoneInvite(invitee: Invitee) {
|
833
834
|
if (!this.locusUrl) {
|
834
835
|
return Promise.reject(
|
835
836
|
new ParameterError('The associated locus url for this meeting object must be defined.')
|
@@ -847,13 +848,13 @@ export default class Members extends StatelessWebexPlugin {
|
|
847
848
|
|
848
849
|
/**
|
849
850
|
* Cancels an SIP/phone call to the associated meeting
|
850
|
-
* @param {
|
851
|
+
* @param {Invitee} invitee
|
851
852
|
* @param {String} invitee.memberId - The memberId of the invitee
|
852
853
|
* @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
|
853
854
|
* @returns {Promise}
|
854
855
|
* @memberof Members
|
855
856
|
*/
|
856
|
-
cancelInviteByMemberId(invitee:
|
857
|
+
cancelInviteByMemberId(invitee: Invitee) {
|
857
858
|
if (!this.locusUrl) {
|
858
859
|
return Promise.reject(
|
859
860
|
new ParameterError('The associated locus url for this meeting object must be defined.')
|
package/src/members/util.ts
CHANGED
@@ -14,6 +14,7 @@ import {
|
|
14
14
|
} from '../constants';
|
15
15
|
|
16
16
|
import {RoleAssignmentOptions, RoleAssignmentRequest, ServerRoleShape} from './types';
|
17
|
+
import {Invitee} from '../meeting/type';
|
17
18
|
|
18
19
|
const MembersUtil = {
|
19
20
|
/**
|
@@ -105,7 +106,7 @@ const MembersUtil = {
|
|
105
106
|
return requestParams;
|
106
107
|
},
|
107
108
|
|
108
|
-
isInvalidInvitee: (invitee) => {
|
109
|
+
isInvalidInvitee: (invitee: Invitee) => {
|
109
110
|
if (!(invitee && (invitee.email || invitee.emailAddress || invitee.phoneNumber))) {
|
110
111
|
return true;
|
111
112
|
}
|
@@ -56,6 +56,7 @@ import * as MeetingRequestImport from '@webex/plugin-meetings/src/meeting/reques
|
|
56
56
|
import LocusInfo from '@webex/plugin-meetings/src/locus-info';
|
57
57
|
import MediaProperties from '@webex/plugin-meetings/src/media/properties';
|
58
58
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
59
|
+
import MembersUtil from '@webex/plugin-meetings/src/members/util';
|
59
60
|
import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
|
60
61
|
import Media from '@webex/plugin-meetings/src/media/index';
|
61
62
|
import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
|
@@ -610,7 +611,6 @@ describe('plugin-meetings', () => {
|
|
610
611
|
assert.isFalse(meeting.isLocusCall());
|
611
612
|
});
|
612
613
|
});
|
613
|
-
|
614
614
|
describe('#invite', () => {
|
615
615
|
it('should have #invite', () => {
|
616
616
|
assert.exists(meeting.invite);
|
@@ -621,8 +621,6 @@ describe('plugin-meetings', () => {
|
|
621
621
|
it('should proxy members #addMember and return a promise', async () => {
|
622
622
|
const invite = meeting.invite(uuid1, false);
|
623
623
|
|
624
|
-
assert.exists(invite.then);
|
625
|
-
await invite;
|
626
624
|
assert.calledOnce(meeting.members.addMember);
|
627
625
|
assert.calledWith(meeting.members.addMember, uuid1, false);
|
628
626
|
});
|
@@ -12433,7 +12431,7 @@ describe('plugin-meetings', () => {
|
|
12433
12431
|
activeSharingId.whiteboard = beneficiaryId;
|
12434
12432
|
|
12435
12433
|
eventTrigger.share.push(
|
12436
|
-
meeting.webinar.selfIsAttendee
|
12434
|
+
meeting.webinar.selfIsAttendee || meeting.guest
|
12437
12435
|
? {
|
12438
12436
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
12439
12437
|
functionName: 'remoteShare',
|
@@ -12452,7 +12450,8 @@ describe('plugin-meetings', () => {
|
|
12452
12450
|
}
|
12453
12451
|
);
|
12454
12452
|
|
12455
|
-
shareStatus =
|
12453
|
+
shareStatus =
|
12454
|
+
meeting.webinar.selfIsAttendee || meeting.guest
|
12456
12455
|
? SHARE_STATUS.REMOTE_SHARE_ACTIVE
|
12457
12456
|
: SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
12458
12457
|
}
|
@@ -12670,6 +12669,36 @@ describe('plugin-meetings', () => {
|
|
12670
12669
|
});
|
12671
12670
|
});
|
12672
12671
|
|
12672
|
+
describe('Whiteboard Share - User is guest', () => {
|
12673
|
+
it('User receives a remote share instead of whiteboard share', () => {
|
12674
|
+
// Set the guest flag
|
12675
|
+
meeting.guest = true;
|
12676
|
+
|
12677
|
+
// Step 1: Start sharing whiteboard A
|
12678
|
+
const data1 = generateData(
|
12679
|
+
blankPayload, // Initial payload
|
12680
|
+
true, // isGranting: Granting share
|
12681
|
+
false, // isContent: Whiteboard (not content)
|
12682
|
+
USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
|
12683
|
+
RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
|
12684
|
+
);
|
12685
|
+
|
12686
|
+
// Step 2: Stop sharing whiteboard A
|
12687
|
+
const data2 = generateData(
|
12688
|
+
data1.payload, // Updated payload from Step 1
|
12689
|
+
false, // isGranting: Stopping share
|
12690
|
+
false, // isContent: Whiteboard
|
12691
|
+
USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
|
12692
|
+
);
|
12693
|
+
|
12694
|
+
// Validate the payload changes and status updates
|
12695
|
+
payloadTestHelper([data1]);
|
12696
|
+
|
12697
|
+
// Specific assertions for guest
|
12698
|
+
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
12699
|
+
});
|
12700
|
+
});
|
12701
|
+
|
12673
12702
|
describe('Whiteboard A --> Whiteboard B', () => {
|
12674
12703
|
it('Scenario #1: you share both whiteboards', () => {
|
12675
12704
|
const data1 = generateData(
|
@@ -177,6 +177,26 @@ describe('plugin-meetings', () => {
|
|
177
177
|
assert.isFalse(MembersUtil.isInvalidInvitee({email: 'sip:test@cisco.com'}), 'SIP email should be valid');
|
178
178
|
});
|
179
179
|
|
180
|
+
it('should skip email validation if skipEmailValidation is true', async () => {
|
181
|
+
sandbox.spy(MembersUtil, 'isInvalidInvitee');
|
182
|
+
|
183
|
+
const members = createMembers({url: true});
|
184
|
+
|
185
|
+
await members.addMember({email: '8618578675309', skipEmailValidation: true});
|
186
|
+
|
187
|
+
assert.notCalled(MembersUtil.isInvalidInvitee);
|
188
|
+
});
|
189
|
+
|
190
|
+
it('should not skip email validation if skipEmailValidation is not equal true', async () => {
|
191
|
+
sandbox.spy(MembersUtil, 'isInvalidInvitee');
|
192
|
+
|
193
|
+
const members = createMembers({url: true});
|
194
|
+
|
195
|
+
await members.addMember({email: '86185786@ds.com'});
|
196
|
+
|
197
|
+
assert.called(MembersUtil.isInvalidInvitee);
|
198
|
+
});
|
199
|
+
|
180
200
|
it('should accept valid phone with isInternalNumber', async () => {
|
181
201
|
sandbox.spy(MembersUtil, 'isInvalidInvitee');
|
182
202
|
|
@@ -190,6 +210,22 @@ describe('plugin-meetings', () => {
|
|
190
210
|
assert.isFalse(MembersUtil.isInvalidInvitee({phoneNumber: '18578675309', isInternalNumber: true}));
|
191
211
|
assert.isTrue(MembersUtil.isInvalidInvitee({phoneNumber: '+8618578675309', isInternalNumber: true}));
|
192
212
|
});
|
213
|
+
|
214
|
+
it('should not crash if params is undefined', async () => {
|
215
|
+
sandbox.spy(MembersUtil, 'isInvalidInvitee');
|
216
|
+
|
217
|
+
const members = createMembers({url: true});
|
218
|
+
|
219
|
+
try {
|
220
|
+
await members.addMember(undefined);
|
221
|
+
} catch (err) {
|
222
|
+
assert.instanceOf(err, ParameterError);
|
223
|
+
|
224
|
+
assert.equal(err.message, 'The invitee must be defined with either a valid email, emailAddress or phoneNumber property.');
|
225
|
+
}
|
226
|
+
|
227
|
+
assert.called(MembersUtil.isInvalidInvitee);
|
228
|
+
});
|
193
229
|
});
|
194
230
|
|
195
231
|
describe('#admitMembers', () => {
|