@webex/plugin-meetings 1.157.1 → 1.159.1

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/src/config.js CHANGED
@@ -89,7 +89,8 @@ export default {
89
89
  enableExtmap: false,
90
90
  experimental: {
91
91
  enableMediaNegotiatedEvent: false,
92
- enableUnifiedMeetings: false
92
+ enableUnifiedMeetings: false,
93
+ enableAdhocMeetings: false
93
94
  }
94
95
  }
95
96
  };
package/src/index.js CHANGED
@@ -10,5 +10,4 @@ registerPlugin('meetings', Meetings, {
10
10
 
11
11
  export default Meetings;
12
12
 
13
- // eslint-disable-next-line object-curly-spacing
14
13
  export * as CONSTANTS from './constants';
@@ -1,10 +1,11 @@
1
1
 
2
- import {HTTP_VERBS} from '../constants';
2
+ import {HTTP_VERBS, _CONVERSATION_URL_} from '../constants';
3
3
 
4
4
  import MeetingInfoUtil from './utilv2';
5
5
 
6
6
  const PASSWORD_ERROR_DEFAULT_MESSAGE = 'Password required. Call fetchMeetingInfo() with password argument';
7
7
  const CAPTCHA_ERROR_DEFAULT_MESSAGE = 'Captcha required. Call fetchMeetingInfo() with captchaInfo argument';
8
+ const ADHOC_MEETING_DEFAULT_ERROR = 'Failed starting the adhoc meeting, Please contact support team ';
8
9
 
9
10
  /**
10
11
  * Error to indicate that wbxappapi requires a password
@@ -28,7 +29,27 @@ export class MeetingInfoV2PasswordError extends Error {
28
29
  }
29
30
 
30
31
  /**
31
- * Error to indicate that wbxappapi requires a captcha
32
+ * Error generating a adhoc space meeting
33
+ */
34
+ export class MeetingInfoV2AdhocMeetingError extends Error {
35
+ /**
36
+ *
37
+ * @constructor
38
+ * @param {Number} [wbxAppApiErrorCode]
39
+ * @param {String} [message]
40
+ */
41
+ constructor(wbxAppApiErrorCode, message = ADHOC_MEETING_DEFAULT_ERROR) {
42
+ super(`${message}, code=${wbxAppApiErrorCode}`);
43
+ this.name = 'MeetingInfoV2AdhocMeetingError';
44
+ this.sdkMessage = message;
45
+ this.stack = (new Error()).stack;
46
+ this.wbxAppApiCode = wbxAppApiErrorCode;
47
+ }
48
+ }
49
+
50
+
51
+ /**
52
+ * Error to indicate that preferred webex site not present to start adhoc meeting
32
53
  */
33
54
  export class MeetingInfoV2CaptchaError extends Error {
34
55
  /**
@@ -65,7 +86,7 @@ export default class MeetingInfoV2 {
65
86
  * converts hydra id into conversation url and persons Id
66
87
  * @param {String} destination one of many different types of destinations to look up info for
67
88
  * @param {String} [type] to match up with the destination value
68
- * @returns {Promise} returns destination and type
89
+ * @returns {Promise} destination and type
69
90
  * @public
70
91
  * @memberof MeetingInfo
71
92
  */
@@ -77,6 +98,59 @@ export default class MeetingInfoV2 {
77
98
  });
78
99
  }
79
100
 
101
+ /**
102
+ * Creates adhoc space meetings for a space by fetching the conversation infomation
103
+ * @param {String} conversationUrl conversationUrl to start adhoc meeting on
104
+ * @returns {Promise} returns a meeting info object
105
+ * @public
106
+ * @memberof MeetingInfo
107
+ */
108
+ async createAdhocSpaceMeeting(conversationUrl) {
109
+ if (!this.webex.meetings.preferredWebexSite) {
110
+ throw Error('No preferred webex site found');
111
+ }
112
+ const getInvitees = (particpants = []) => {
113
+ const invitees = [];
114
+
115
+ if (particpants) {
116
+ particpants.forEach((participant) => {
117
+ invitees.push({
118
+ email: participant.emailAddress,
119
+ ciUserUuid: participant.entryUUID
120
+ });
121
+ });
122
+ }
123
+
124
+ return invitees;
125
+ };
126
+
127
+ return this.webex.internal.conversation.get(
128
+ {url: conversationUrl},
129
+ {includeParticipants: true, disableTransform: true}
130
+ )
131
+ .then((conversation) => {
132
+ const body = {
133
+ title: conversation.displayName,
134
+ spaceUrl: conversation.url,
135
+ keyUrl: conversation.encryptionKeyUrl,
136
+ kroUrl: conversation.kmsResourceObjectUrl,
137
+ invitees: getInvitees(conversation.participants?.items)
138
+ };
139
+
140
+ const uri = this.webex.meetings.preferredWebexSite ?
141
+ `https://${this.webex.meetings.preferredWebexSite}/wbxappapi/v2/meetings/spaceInstant` : '';
142
+
143
+ return this.webex.request({
144
+ method: HTTP_VERBS.POST,
145
+ uri,
146
+ body
147
+ })
148
+ .catch((err) => {
149
+ throw new MeetingInfoV2AdhocMeetingError(err.body?.code, err.body?.message);
150
+ });
151
+ });
152
+ }
153
+
80
154
  /**
81
155
  * Fetches meeting info from the server
82
156
  * @param {String} destination one of many different types of destinations to look up info for
@@ -95,14 +169,25 @@ export default class MeetingInfoV2 {
95
169
  type,
96
170
  webex: this.webex
97
171
  });
172
+
173
+ if (destinationType.type === _CONVERSATION_URL_ && this.webex.config.meetings.experimental.enableAdhocMeetings) {
174
+ return this.createAdhocSpaceMeeting(destinationType.destination);
175
+ }
176
+
98
177
  const body = await MeetingInfoUtil.getRequestBody({...destinationType, password, captchaInfo});
99
178
 
100
- return this.webex.request({
179
+ const options = {
101
180
  method: HTTP_VERBS.POST,
102
181
  service: 'webex-appapi-service',
103
182
  resource: 'meetingInfo',
104
183
  body
105
- })
184
+ };
185
+
186
+ const directURI = await MeetingInfoUtil.getDirectMeetingInfoURI(destinationType);
187
+
188
+ if (directURI) options.directURI = directURI;
189
+
190
+ return this.webex.request(options)
106
191
  .catch((err) => {
107
192
  if (err?.statusCode === 403) {
108
193
  throw new MeetingInfoV2PasswordError(err.body?.code, err.body?.data?.meetingInfo);
@@ -264,4 +264,43 @@ MeetingInfoUtil.getRequestBody = (options) => {
264
264
  return body;
265
265
  };
266
266
 
267
+ /**
268
+ * Helper function to parse the webex site/host from a URI string.
269
+ * @param {String} uri string (e.g. '10019857020@convergedats.webex.com')
270
+ * @returns {String} the site/host part of the URI string (e.g. 'convergedats.webex.com')
271
+ */
272
+ MeetingInfoUtil.getWebexSite = (uri) => {
273
+ const exceptedDomains = ['meet.webex.com', 'meetup.webex.com', 'ciscospark.com'];
274
+ const site = uri?.match(/.+@(.+\..+\.*.)$/)?.[1];
275
+
276
+ return exceptedDomains.includes(site) ? null : site;
277
+ };
278
+
279
+ /**
280
+ * Helper function to return the direct URI for fetching meeting info (to avoid a redirect).
281
+ * @param {Object} options type and value to fetch meeting info
282
+ * @param {String} options.type One of [SIP_URI, PERSONAL_ROOM, MEETING_ID, CONVERSATION_URL, LOCUS_ID, MEETING_LINK]
283
+ * @param {Object} options.destination ?? value.value
284
+ * @returns {String} returns a URI string or null of there is no direct URI
285
+ */
286
+ MeetingInfoUtil.getDirectMeetingInfoURI = (options) => {
287
+ const {
288
+ type, destination
289
+ } = options;
290
+
291
+ let preferredWebexSite = null;
292
+
293
+ switch (type) {
294
+ case _SIP_URI_:
295
+ preferredWebexSite = MeetingInfoUtil.getWebexSite(destination);
296
+ break;
297
+ case _LOCUS_ID_:
298
+ preferredWebexSite = destination.info?.webExSite;
299
+ break;
300
+ default:
301
+ }
302
+
303
+ return preferredWebexSite ? `https://${preferredWebexSite}/wbxappapi/v1/meetingInfo` : null;
304
+ };
305
+
267
306
  export default MeetingInfoUtil;
@@ -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.
@@ -176,8 +186,6 @@ export default class Meetings extends WebexPlugin {
176
186
  getSupportedDevice: Media.getSupportedDevice
177
187
  };
178
188
 
179
- LoggerProxy.set(this.webex.logger);
180
-
181
189
  this.onReady();
182
190
  MeetingsUtil.checkH264Support({disableNotifications: true});
183
191
  Metrics.initialSetup(this.meetingCollection, this.webex);
@@ -368,6 +376,7 @@ export default class Meetings extends WebexPlugin {
368
376
  onReady() {
369
377
  this.webex.once(READY, () => {
370
378
  StaticConfig.set(this.config);
379
+ LoggerProxy.set(this.webex.logger);
371
380
  LoggerConfig.set(this.config.logging);
372
381
 
373
382
  /**
@@ -408,6 +417,22 @@ export default class Meetings extends WebexPlugin {
408
417
  }
409
418
  }
410
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
+
411
436
  /**
412
437
  * Explicitly sets up the meetings plugin by registering
413
438
  * the device, connecting to mercury, and listening for locus events.
@@ -431,6 +456,7 @@ export default class Meetings extends WebexPlugin {
431
456
  }
432
457
 
433
458
  return Promise.all([
459
+ this.fetchUserPreferredWebexSite(),
434
460
  this.getGeoHint(),
435
461
  this.startReachability().catch((error) => {
436
462
  LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
@@ -605,12 +631,29 @@ export default class Meetings extends WebexPlugin {
605
631
  });
606
632
  }
607
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
+ if (res && res.userPreferences) {
644
+ this.preferredWebexSite = MeetingsUtil.parseUserPreferences(res?.userPreferences);
645
+ }
646
+ });
647
+ }
648
+
649
+
608
650
  /**
609
651
  * gets the personal meeting room instance, for saved PMR values for this user
610
652
  * @returns {PersonalMeetingRoom}
611
653
  * @public
612
654
  * @memberof Meetings
613
655
  */
656
+
614
657
  getPersonalMeetingRoom() {
615
658
  return this.personalMeetingRoom;
616
659
  }
@@ -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.
@@ -1,7 +1,6 @@
1
- import 'jsdom-global/register';
1
+ import Transcription from '@webex/plugin-meetings/src/transcription';
2
2
  import {assert} from '@webex/test-helper-chai';
3
3
  import sinon from 'sinon';
4
- import Transcription from '@webex/plugin-meetings/src/transcription';
5
4
 
6
5
  describe('transcription index', () => {
7
6
  let webSocketUrl, members, sessionId, token, transcription;
@@ -82,8 +82,28 @@ describe('plugin-meetings', () => {
82
82
  Object.defineProperty(global.window.navigator, 'mediaDevices', {
83
83
  writable: true,
84
84
  value: {
85
- getDisplayMedia: sinon.stub().returns(Promise.resolve(MediaStream))
86
- }
85
+ getDisplayMedia: sinon.stub().returns(Promise.resolve(MediaStream)),
86
+ enumerateDevices: sinon.stub().returns(Promise.resolve([
87
+ {
88
+ deviceId: '',
89
+ kind: 'audioinput',
90
+ label: '',
91
+ groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
92
+ },
93
+ {
94
+ deviceId: '',
95
+ kind: 'videoinput',
96
+ label: '',
97
+ groupId: '08d4f8200e7e4a3425ecf75b7edea9ae4acd934019f2a52217554bcc8e46604d',
98
+ },
99
+ {
100
+ deviceId: '',
101
+ kind: 'audiooutput',
102
+ label: '',
103
+ groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
104
+ }
105
+ ])),
106
+ },
87
107
  });
88
108
 
89
109
  Object.defineProperty(global.window, 'MediaStream', {
@@ -143,6 +163,7 @@ describe('plugin-meetings', () => {
143
163
  Metrics.postEvent = sinon.stub();
144
164
  Metrics.initialSetup(null, webex);
145
165
  MediaUtil.createPeerConnection = sinon.stub().returns({});
166
+ MediaUtil.createMediaStream = sinon.stub().returns(true);
146
167
 
147
168
  uuid1 = uuid.v4();
148
169
  uuid2 = uuid.v4();
@@ -2551,7 +2572,6 @@ describe('plugin-meetings', () => {
2551
2572
  sandbox.stub(MeetingUtil, 'getTrack').returns({audioTrack: null, videoTrack: fakeTrack});
2552
2573
  sandbox.stub(meeting.mediaProperties, 'setMediaSettings');
2553
2574
  sandbox.stub(meeting.mediaProperties, 'setVideoDeviceId');
2554
- sandbox.stub(MediaUtil, 'createMediaStream').returns(true);
2555
2575
 
2556
2576
  meeting.setLocalTracks(fakeStream);
2557
2577
 
@@ -2592,7 +2612,6 @@ describe('plugin-meetings', () => {
2592
2612
  });
2593
2613
  describe('#setRemoteStream', () => {
2594
2614
  beforeEach(() => {
2595
- MediaUtil.createMediaStream = sinon.stub().returns(true);
2596
2615
  meeting.statsAnalyzer = {startAnalyzer: sinon.stub()};
2597
2616
  });
2598
2617
  it('should trigger a media:ready event when remote stream track ontrack is fired', () => {
@@ -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
  });
@@ -211,5 +211,77 @@ describe('plugin-meetings', () => {
211
211
  assert.equal(res.conversationUrl, 'https://conv-a.wbx2.com/conversation/api/v1/conversations/bfb49280');
212
212
  });
213
213
  });
214
+
215
+ describe('#getWebexSite', () => {
216
+ it('SIP meeting address', () => {
217
+ assert.equal(MeetingInfoUtil.getWebexSite('10019857020@convergedats.webex.com'), 'convergedats.webex.com');
218
+ });
219
+ it('SIP meeting address from excepted domain', () => {
220
+ assert.equal(MeetingInfoUtil.getWebexSite('10019857020@meet.webex.com'), null);
221
+ });
222
+ it('invalid domain', () => {
223
+ assert.equal(MeetingInfoUtil.getWebexSite('invaliddomain'), null);
224
+ });
225
+ });
226
+
227
+ describe('#getDirectMeetingInfoURI', () => {
228
+ it('for _SIP_URI_', () => {
229
+ assert.equal(MeetingInfoUtil.getDirectMeetingInfoURI({
230
+ type: _SIP_URI_,
231
+ destination: 'testing@convergedats.webex.com'
232
+ }), 'https://convergedats.webex.com/wbxappapi/v1/meetingInfo');
233
+ });
234
+
235
+ it('for _LOCUS_ID_ with webExSite', () => {
236
+ assert.equal(MeetingInfoUtil.getDirectMeetingInfoURI({
237
+ type: _LOCUS_ID_,
238
+ destination: {
239
+ info: {
240
+ webExMeetingId: '123456',
241
+ webExSite: 'convergedats.webex.com'
242
+ }
243
+ }
244
+ }), 'https://convergedats.webex.com/wbxappapi/v1/meetingInfo');
245
+ });
246
+
247
+ // null means fall back to default meeting info URI
248
+ it('for _PERSONAL_ROOM_', () => {
249
+ assert.equal(MeetingInfoUtil.getDirectMeetingInfoURI({
250
+ type: _PERSONAL_ROOM_,
251
+ destination: {
252
+ userId: '01824b9b-adef-4b10-b5c1-8a2fe2fb7c0e',
253
+ orgId: '1eb65fdf-9643-417f-9974-ad72cae0e10f'
254
+ }
255
+ }), null);
256
+ });
257
+
258
+ it('for _MEETING_ID_', () => {
259
+ assert.equal(MeetingInfoUtil.getDirectMeetingInfoURI({
260
+ type: _MEETING_ID_,
261
+ destination: '1234323'
262
+ }), null);
263
+ });
264
+
265
+ it('for _MEETING_UUID_', () => {
266
+ assert.equal(MeetingInfoUtil.getDirectMeetingInfoURI({
267
+ type: _MEETING_UUID_,
268
+ destination: 'xsddsdsdsdssdsdsdsdsd'
269
+ }), null);
270
+ });
271
+
272
+ it('for _LOCUS_ID_ with sipUri with excepted domain', () => {
273
+ assert.equal(MeetingInfoUtil.getDirectMeetingInfoURI({
274
+ type: _LOCUS_ID_,
275
+ destination: {info: {webExMeetingId: '123456', sipUri: 'testing@meetup.webex.com'}}
276
+ }), null);
277
+ });
278
+
279
+ it('for _CONVERSATION_URL_', () => {
280
+ assert.equal(MeetingInfoUtil.getDirectMeetingInfoURI({
281
+ type: _CONVERSATION_URL_,
282
+ destination: 'https://conv-a.wbx2.com/conversation/api/v1/conversations/bfb49280'
283
+ }), null);
284
+ });
285
+ });
214
286
  });
215
287
  });