@webex/plugin-meetings 1.158.0 → 1.159.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/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +165 -38
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +65 -15
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/request.js +16 -6
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +17 -0
- package/dist/meetings/util.js.map +1 -1
- package/package.json +6 -5
- package/src/config.js +2 -1
- package/src/meeting-info/meeting-info-v2.js +82 -3
- package/src/meetings/index.js +43 -0
- package/src/meetings/request.js +15 -6
- package/src/meetings/util.js +19 -0
- package/test/unit/spec/meeting-info/meetinginfov2.js +99 -2
- package/test/unit/spec/meetings/index.js +62 -17
- package/test/unit/spec/meetings/utils.js +18 -0
package/src/meetings/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import '@webex/internal-plugin-mercury';
|
|
6
|
+
import '@webex/internal-plugin-conversation';
|
|
6
7
|
import {WebexPlugin} from '@webex/webex-core';
|
|
7
8
|
|
|
8
9
|
import 'webrtc-adapter';
|
|
@@ -163,6 +164,15 @@ export default class Meetings extends WebexPlugin {
|
|
|
163
164
|
*/
|
|
164
165
|
this.registered = false;
|
|
165
166
|
|
|
167
|
+
/**
|
|
168
|
+
* This values indicates the preferred webex site the user will start there meeting, getsits value from {@link Meetings#register}
|
|
169
|
+
* @instance
|
|
170
|
+
* @type {String}
|
|
171
|
+
* @private
|
|
172
|
+
* @memberof Meetings
|
|
173
|
+
*/
|
|
174
|
+
this.preferredWebexSite = '';
|
|
175
|
+
|
|
166
176
|
/**
|
|
167
177
|
* The public interface for the internal Media util files. These are helpful to expose outside the context
|
|
168
178
|
* of a meeting so that a user can access media without creating a meeting instance.
|
|
@@ -407,6 +417,22 @@ export default class Meetings extends WebexPlugin {
|
|
|
407
417
|
}
|
|
408
418
|
}
|
|
409
419
|
|
|
420
|
+
/**
|
|
421
|
+
* API to toggle starting adhoc meeting
|
|
422
|
+
* @param {Boolean} changeState
|
|
423
|
+
* @private
|
|
424
|
+
* @memberof Meetings
|
|
425
|
+
* @returns {undefined}
|
|
426
|
+
*/
|
|
427
|
+
_toggleAdhocMeetings(changeState) {
|
|
428
|
+
if (typeof changeState !== 'boolean') {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (this.config?.experimental?.enableAdhocMeetings !== changeState) {
|
|
432
|
+
this.config.experimental.enableAdhocMeetings = changeState;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
410
436
|
/**
|
|
411
437
|
* Explicitly sets up the meetings plugin by registering
|
|
412
438
|
* the device, connecting to mercury, and listening for locus events.
|
|
@@ -430,6 +456,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
430
456
|
}
|
|
431
457
|
|
|
432
458
|
return Promise.all([
|
|
459
|
+
this.fetchUserPreferredWebexSite(),
|
|
433
460
|
this.getGeoHint(),
|
|
434
461
|
this.startReachability().catch((error) => {
|
|
435
462
|
LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
|
|
@@ -604,12 +631,28 @@ export default class Meetings extends WebexPlugin {
|
|
|
604
631
|
});
|
|
605
632
|
}
|
|
606
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Fetch user preferred webex site information
|
|
636
|
+
* This also has other infomation about the user
|
|
637
|
+
* @returns {Promise}
|
|
638
|
+
* @private
|
|
639
|
+
* @memberof Meetings
|
|
640
|
+
*/
|
|
641
|
+
fetchUserPreferredWebexSite() {
|
|
642
|
+
return this.request.fetchLoginUserInformation().then((res) => {
|
|
643
|
+
this.preferredWebexSite = MeetingsUtil.parseUserPreferences(res.userPreferences);
|
|
644
|
+
console.error(this.preferredWebexSite);
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
|
|
607
649
|
/**
|
|
608
650
|
* gets the personal meeting room instance, for saved PMR values for this user
|
|
609
651
|
* @returns {PersonalMeetingRoom}
|
|
610
652
|
* @public
|
|
611
653
|
* @memberof Meetings
|
|
612
654
|
*/
|
|
655
|
+
|
|
613
656
|
getPersonalMeetingRoom() {
|
|
614
657
|
return this.personalMeetingRoom;
|
|
615
658
|
}
|
package/src/meetings/request.js
CHANGED
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
*/
|
|
13
13
|
export default class MeetingRequest extends StatelessWebexPlugin {
|
|
14
14
|
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
* get all the active meetings for the user
|
|
16
|
+
* @returns {Array} return locus array
|
|
17
|
+
*/
|
|
18
18
|
getActiveMeetings() {
|
|
19
19
|
return this.request({
|
|
20
20
|
api: API.LOCUS,
|
|
@@ -27,13 +27,22 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
* fetch geoHit for the user
|
|
31
|
+
* @returns {Promise<object>} geoHintInfo
|
|
32
|
+
*/
|
|
33
33
|
fetchGeoHint() {
|
|
34
34
|
return this.webex.internal.services.fetchClientRegionInfo();
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* fetch login user information
|
|
39
|
+
* @returns {Promise<object>} loginUserInformation
|
|
40
|
+
*/
|
|
41
|
+
fetchLoginUserInformation() {
|
|
42
|
+
return this.webex.internal.services.fetchLoginUserInformation();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
37
46
|
// locus federation, determines and populate locus if the responseBody has remote URLs to fetch locus details
|
|
38
47
|
|
|
39
48
|
/**
|
package/src/meetings/util.js
CHANGED
|
@@ -64,6 +64,25 @@ MeetingsUtil.checkForCorrelationId = (deviceUrl, locus) => {
|
|
|
64
64
|
return false;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
MeetingsUtil.parseUserPreferences = (userPreferences) => {
|
|
68
|
+
let webexSite = null;
|
|
69
|
+
|
|
70
|
+
userPreferences.find((item) => {
|
|
71
|
+
// eslint-disable-next-line no-useless-escape
|
|
72
|
+
const regex = /"preferredWebExSite\":\"(\S+)\"/;
|
|
73
|
+
const preferredSite = item.match(regex);
|
|
74
|
+
|
|
75
|
+
if (preferredSite) {
|
|
76
|
+
webexSite = preferredSite[1];
|
|
77
|
+
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return false;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return webexSite;
|
|
85
|
+
};
|
|
67
86
|
|
|
68
87
|
/**
|
|
69
88
|
* Will check to see if the H.264 media codec is supported.
|
|
@@ -10,12 +10,34 @@ import Mercury from '@webex/internal-plugin-mercury';
|
|
|
10
10
|
import Meetings from '@webex/plugin-meetings/src/meetings';
|
|
11
11
|
import {
|
|
12
12
|
_MEETING_ID_,
|
|
13
|
-
_PERSONAL_ROOM_
|
|
13
|
+
_PERSONAL_ROOM_,
|
|
14
|
+
_CONVERSATION_URL_
|
|
14
15
|
} from '@webex/plugin-meetings/src/constants';
|
|
15
|
-
import MeetingInfo, {MeetingInfoV2PasswordError, MeetingInfoV2CaptchaError} from '@webex/plugin-meetings/src/meeting-info/meeting-info-v2';
|
|
16
|
+
import MeetingInfo, {MeetingInfoV2PasswordError, MeetingInfoV2CaptchaError, MeetingInfoV2AdhocMeetingError} from '@webex/plugin-meetings/src/meeting-info/meeting-info-v2';
|
|
16
17
|
import MeetingInfoUtil from '@webex/plugin-meetings/src/meeting-info/utilv2';
|
|
17
18
|
|
|
18
19
|
describe('plugin-meetings', () => {
|
|
20
|
+
const conversation = {
|
|
21
|
+
displayName: 'displayName',
|
|
22
|
+
url: 'conversationUrl',
|
|
23
|
+
encryptionKeyUrl: 'encryptionKeyUrl',
|
|
24
|
+
kmsResourceObjectUrl: 'kmsResourceObjectUrl',
|
|
25
|
+
participants: {
|
|
26
|
+
items: [
|
|
27
|
+
{
|
|
28
|
+
id: '344ea183-9d5d-4e77-aed',
|
|
29
|
+
emailAddress: 'testUser1@cisco.com',
|
|
30
|
+
entryUUID: '344ea183-9d5d-4e77-'
|
|
31
|
+
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: '40b446fe-175c-4628-8a9d',
|
|
35
|
+
emailAddress: 'testUser2@cisco.com',
|
|
36
|
+
entryUUID: '40b446fe-175c-4628'
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
};
|
|
19
41
|
let webex;
|
|
20
42
|
let meetingInfo = null;
|
|
21
43
|
|
|
@@ -29,6 +51,9 @@ describe('plugin-meetings', () => {
|
|
|
29
51
|
}
|
|
30
52
|
});
|
|
31
53
|
|
|
54
|
+
webex.meetings.preferredWebexSite = 'go.webex.com';
|
|
55
|
+
webex.config.meetings = {experimental: {enableUnifiedMeetings: true, enableAdhocMeetings: true}};
|
|
56
|
+
|
|
32
57
|
Object.assign(webex.internal, {
|
|
33
58
|
device: {
|
|
34
59
|
deviceType: 'FAKE_DEVICE',
|
|
@@ -41,6 +66,9 @@ describe('plugin-meetings', () => {
|
|
|
41
66
|
disconnect: sinon.stub().returns(Promise.resolve()),
|
|
42
67
|
on: () => {},
|
|
43
68
|
off: () => {}
|
|
69
|
+
},
|
|
70
|
+
conversation: {
|
|
71
|
+
get: sinon.stub().returns(Promise.resolve(conversation))
|
|
44
72
|
}
|
|
45
73
|
});
|
|
46
74
|
|
|
@@ -98,6 +126,40 @@ describe('plugin-meetings', () => {
|
|
|
98
126
|
});
|
|
99
127
|
});
|
|
100
128
|
|
|
129
|
+
it('create adhoc meeting when conversationUrl passed with enableAdhocMeetings toggle', async () => {
|
|
130
|
+
sinon.stub(meetingInfo, 'createAdhocSpaceMeeting').returns(Promise.resolve());
|
|
131
|
+
await meetingInfo.fetchMeetingInfo('conversationUrl', _CONVERSATION_URL_);
|
|
132
|
+
|
|
133
|
+
assert.calledWith(meetingInfo.createAdhocSpaceMeeting, 'conversationUrl');
|
|
134
|
+
assert.notCalled(webex.request);
|
|
135
|
+
meetingInfo.createAdhocSpaceMeeting.restore();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should not call createAdhocSpaceMeeting if enableAdhocMeetings toggle is off', async () => {
|
|
139
|
+
webex.config.meetings.experimental.enableAdhocMeetings = false;
|
|
140
|
+
sinon.stub(meetingInfo, 'createAdhocSpaceMeeting').returns(Promise.resolve());
|
|
141
|
+
|
|
142
|
+
await meetingInfo.fetchMeetingInfo('conversationUrl', _CONVERSATION_URL_);
|
|
143
|
+
|
|
144
|
+
assert.notCalled(meetingInfo.createAdhocSpaceMeeting);
|
|
145
|
+
assert.called(webex.request);
|
|
146
|
+
meetingInfo.createAdhocSpaceMeeting.restore();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should throw an error MeetingInfoV2AdhocMeetingError if not able to start adhoc meeting for a conversation', async () => {
|
|
150
|
+
webex.config.meetings.experimental.enableAdhocMeetings = true;
|
|
151
|
+
|
|
152
|
+
webex.request = sinon.stub().rejects({statusCode: 403, body: {code: 400000, message: 'Input is invalid'}});
|
|
153
|
+
try {
|
|
154
|
+
await meetingInfo.createAdhocSpaceMeeting('conversationUrl');
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
assert.instanceOf(err, MeetingInfoV2AdhocMeetingError);
|
|
158
|
+
assert.deepEqual(err.message, 'Input is invalid, code=400000');
|
|
159
|
+
assert.equal(err.wbxAppApiCode, 400000);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
101
163
|
it('should throw MeetingInfoV2PasswordError for 403 response', async () => {
|
|
102
164
|
const FAKE_MEETING_INFO = {blablabla: 'some_fake_meeting_info'};
|
|
103
165
|
|
|
@@ -154,5 +216,40 @@ describe('plugin-meetings', () => {
|
|
|
154
216
|
});
|
|
155
217
|
});
|
|
156
218
|
});
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
describe('createAdhocSpaceMeeting', () => {
|
|
222
|
+
it('Make a request to /instantSpace when conversationUrl', async () => {
|
|
223
|
+
const conversationUrl = 'https://conversationUrl/xxx';
|
|
224
|
+
const invitee = [];
|
|
225
|
+
|
|
226
|
+
invitee.push({
|
|
227
|
+
email: conversation.participants.items[0].emailAddress,
|
|
228
|
+
ciUserUuid: conversation.participants.items[0].entryUUID
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
invitee.push({
|
|
232
|
+
email: conversation.participants.items[1].emailAddress,
|
|
233
|
+
ciUserUuid: conversation.participants.items[1].entryUUID
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
await meetingInfo.createAdhocSpaceMeeting(conversationUrl);
|
|
237
|
+
|
|
238
|
+
assert.calledWith(webex.internal.conversation.get, {url: conversationUrl},
|
|
239
|
+
{includeParticipants: true, disableTransform: true});
|
|
240
|
+
|
|
241
|
+
assert.calledWith(webex.request, {
|
|
242
|
+
method: 'POST',
|
|
243
|
+
uri: 'https://go.webex.com/wbxappapi/v2/meetings/spaceInstant',
|
|
244
|
+
body: {
|
|
245
|
+
title: conversation.displayName,
|
|
246
|
+
spaceUrl: conversation.url,
|
|
247
|
+
keyUrl: conversation.encryptionKeyUrl,
|
|
248
|
+
kroUrl: conversation.kmsResourceObjectUrl,
|
|
249
|
+
invitees: invitee
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
157
254
|
});
|
|
158
255
|
});
|
|
@@ -28,7 +28,6 @@ import {
|
|
|
28
28
|
ROAP
|
|
29
29
|
} from '../../../../src/constants';
|
|
30
30
|
|
|
31
|
-
|
|
32
31
|
describe('plugin-meetings', () => {
|
|
33
32
|
const logger = {
|
|
34
33
|
log: () => {},
|
|
@@ -74,6 +73,11 @@ describe('plugin-meetings', () => {
|
|
|
74
73
|
}
|
|
75
74
|
});
|
|
76
75
|
|
|
76
|
+
|
|
77
|
+
Object.assign(webex, {
|
|
78
|
+
logging: logger
|
|
79
|
+
});
|
|
80
|
+
|
|
77
81
|
Object.assign(webex.meetings.config, {
|
|
78
82
|
bandwidth: {
|
|
79
83
|
// please note, these are the maximum bandwidth values
|
|
@@ -86,7 +90,7 @@ describe('plugin-meetings', () => {
|
|
|
86
90
|
enableUnifiedMeetings: true
|
|
87
91
|
},
|
|
88
92
|
logging: {
|
|
89
|
-
enable:
|
|
93
|
+
enable: true,
|
|
90
94
|
verboseEvents: true
|
|
91
95
|
}
|
|
92
96
|
});
|
|
@@ -95,6 +99,10 @@ describe('plugin-meetings', () => {
|
|
|
95
99
|
logger
|
|
96
100
|
});
|
|
97
101
|
|
|
102
|
+
Object.assign(webex.meetings, {
|
|
103
|
+
startReachability: sinon.stub().returns(Promise.resolve())
|
|
104
|
+
});
|
|
105
|
+
|
|
98
106
|
Object.assign(webex.internal, {
|
|
99
107
|
device: {
|
|
100
108
|
deviceType: 'FAKE_DEVICE',
|
|
@@ -162,17 +170,36 @@ describe('plugin-meetings', () => {
|
|
|
162
170
|
});
|
|
163
171
|
});
|
|
164
172
|
|
|
173
|
+
describe('#_toggleAdhocMeetings', () => {
|
|
174
|
+
it('should have toggleAdhocMeetings', () => {
|
|
175
|
+
assert.equal(typeof webex.meetings._toggleAdhocMeetings, 'function');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('success', () => {
|
|
179
|
+
it('should update meetings to start adhoc meeting', () => {
|
|
180
|
+
webex.meetings._toggleAdhocMeetings(false);
|
|
181
|
+
assert.equal(webex.meetings.config.experimental.enableAdhocMeetings, false);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('failure', () => {
|
|
186
|
+
it('should not accept non boolean input', () => {
|
|
187
|
+
const currentEnableAdhocMeetings = webex.meetings.config.experimental.enableAdhocMeetings;
|
|
188
|
+
|
|
189
|
+
webex.meetings._toggleAdhocMeetings('test');
|
|
190
|
+
assert.equal(webex.meetings.config.experimental.enableAdhocMeetings, currentEnableAdhocMeetings);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
|
|
165
196
|
describe('Public API Contracts', () => {
|
|
166
197
|
describe('#register', () => {
|
|
167
|
-
it('emits an event and resolves when register succeeds', (
|
|
198
|
+
it('emits an event and resolves when register succeeds', async () => {
|
|
168
199
|
webex.canAuthorize = true;
|
|
169
|
-
webex.meetings.register()
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}, 'meetings:registered');
|
|
173
|
-
assert.isTrue(webex.meetings.registered);
|
|
174
|
-
done();
|
|
175
|
-
});
|
|
200
|
+
await webex.meetings.register();
|
|
201
|
+
assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {file: 'meetings', function: 'register'}, 'meetings:registered');
|
|
202
|
+
assert.isTrue(webex.meetings.registered);
|
|
176
203
|
});
|
|
177
204
|
|
|
178
205
|
it('rejects when SDK canAuthorize is false', () => {
|
|
@@ -192,15 +219,24 @@ describe('plugin-meetings', () => {
|
|
|
192
219
|
assert.isRejected(webex.meetings.register());
|
|
193
220
|
});
|
|
194
221
|
|
|
195
|
-
it('resolves immediately if already registered', (
|
|
222
|
+
it('resolves immediately if already registered', async () => {
|
|
196
223
|
webex.canAuthorize = true;
|
|
197
224
|
webex.meetings.registered = true;
|
|
198
|
-
webex.meetings.register()
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
225
|
+
await webex.meetings.register();
|
|
226
|
+
assert.notCalled(webex.internal.device.register);
|
|
227
|
+
assert.notCalled(webex.internal.mercury.connect);
|
|
228
|
+
assert.isTrue(webex.meetings.registered);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('on register makes sure following functions are called ', async () => {
|
|
232
|
+
webex.canAuthorize = true;
|
|
233
|
+
webex.meetings.registered = false;
|
|
234
|
+
await webex.meetings.register();
|
|
235
|
+
assert.called(webex.internal.device.register);
|
|
236
|
+
assert.called(webex.internal.services.fetchLoginUserInformation);
|
|
237
|
+
assert.called(webex.internal.services.fetchClientRegionInfo);
|
|
238
|
+
assert.called(webex.internal.mercury.connect);
|
|
239
|
+
assert.isTrue(webex.meetings.registered);
|
|
204
240
|
});
|
|
205
241
|
});
|
|
206
242
|
|
|
@@ -768,6 +804,15 @@ describe('plugin-meetings', () => {
|
|
|
768
804
|
assert.calledWith(TriggerProxy.trigger, webex.internal.mercury, SCOPE, EVENT);
|
|
769
805
|
});
|
|
770
806
|
});
|
|
807
|
+
|
|
808
|
+
describe('#fetchUserPreferredWebexSite', () => {
|
|
809
|
+
it('should call request.fetchLoginUserInformation to get the preferred webex site ', async () => {
|
|
810
|
+
assert.isDefined(webex.meetings.preferredWebexSite);
|
|
811
|
+
await webex.meetings.fetchUserPreferredWebexSite();
|
|
812
|
+
|
|
813
|
+
assert.equal(webex.meetings.preferredWebexSite, 'go.webex.com');
|
|
814
|
+
});
|
|
815
|
+
});
|
|
771
816
|
});
|
|
772
817
|
});
|
|
773
818
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {assert} from '@webex/test-helper-chai';
|
|
2
|
+
import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
|
|
3
|
+
|
|
4
|
+
describe('plugin-meetings', () => {
|
|
5
|
+
describe('Meetings utils function', () => {
|
|
6
|
+
describe('#parseUserPreferences', () => {
|
|
7
|
+
it('parsed the prefered webesite from userPreferences', async () => {
|
|
8
|
+
const res = MeetingsUtil.parseUserPreferences([
|
|
9
|
+
'SparkTOSAccept',
|
|
10
|
+
'"preferredWebExSite":"go.webex.com"'
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
assert.equal(res, 'go.webex.com');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|