@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.
@@ -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
  }
@@ -12,9 +12,9 @@ import {
12
12
  */
13
13
  export default class MeetingRequest extends StatelessWebexPlugin {
14
14
  /**
15
- * get all the active meetings for the user
16
- * @returns {Array} return locus array
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
- * fetch geoHit for the user
31
- * @returns {Promise<object>} geoHintInfo
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
  /**
@@ -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: false,
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', (done) => {
198
+ it('emits an event and resolves when register succeeds', async () => {
168
199
  webex.canAuthorize = true;
169
- webex.meetings.register().then(() => {
170
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {
171
- file: 'meetings', function: 'register'
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', (done) => {
222
+ it('resolves immediately if already registered', async () => {
196
223
  webex.canAuthorize = true;
197
224
  webex.meetings.registered = true;
198
- webex.meetings.register().then(() => {
199
- assert.notCalled(webex.internal.device.register);
200
- assert.notCalled(webex.internal.mercury.connect);
201
- assert.isTrue(webex.meetings.registered);
202
- done();
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
+