@webex/plugin-meetings 3.0.0-beta.112 → 3.0.0-beta.114

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.
@@ -156,14 +156,38 @@ export default class MeetingInfoV2 {
156
156
  });
157
157
  }
158
158
 
159
+ /**
160
+ * Raises a MeetingInfoV2PolicyError for policy error codes
161
+ * @param {any} err the error from the request
162
+ * @returns {void}
163
+ */
164
+ handlePolicyError = (err) => {
165
+ if (!err.body) {
166
+ return;
167
+ }
168
+
169
+ if (POLICY_ERROR_CODES.includes(err.body?.code)) {
170
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_INFO_POLICY_ERROR, {
171
+ code: err.body?.code,
172
+ });
173
+
174
+ throw new MeetingInfoV2PolicyError(
175
+ err.body?.code,
176
+ err.body?.data?.meetingInfo,
177
+ err.body?.message
178
+ );
179
+ }
180
+ };
181
+
159
182
  /**
160
183
  * Creates adhoc space meetings for a space by fetching the conversation infomation
161
184
  * @param {String} conversationUrl conversationUrl to start adhoc meeting on
185
+ * @param {String} installedOrgID org ID of user's machine
162
186
  * @returns {Promise} returns a meeting info object
163
187
  * @public
164
188
  * @memberof MeetingInfo
165
189
  */
166
- async createAdhocSpaceMeeting(conversationUrl: string) {
190
+ async createAdhocSpaceMeeting(conversationUrl: string, installedOrgID?: string) {
167
191
  if (!this.webex.meetings.preferredWebexSite) {
168
192
  throw Error('No preferred webex site found');
169
193
  }
@@ -185,7 +209,14 @@ export default class MeetingInfoV2 {
185
209
  return this.webex.internal.conversation
186
210
  .get({url: conversationUrl}, {includeParticipants: true, disableTransform: true})
187
211
  .then((conversation) => {
188
- const body = {
212
+ const body: {
213
+ title: string;
214
+ spaceUrl: string;
215
+ keyUrl: string;
216
+ kroUrl: string;
217
+ invitees: any[];
218
+ installedOrgID?: string;
219
+ } = {
189
220
  title: conversation.displayName,
190
221
  spaceUrl: conversation.url,
191
222
  keyUrl: conversation.encryptionKeyUrl,
@@ -193,19 +224,28 @@ export default class MeetingInfoV2 {
193
224
  invitees: getInvitees(conversation.participants?.items),
194
225
  };
195
226
 
227
+ if (installedOrgID) {
228
+ body.installedOrgID = installedOrgID;
229
+ }
230
+
196
231
  const uri = this.webex.meetings.preferredWebexSite
197
232
  ? `https://${this.webex.meetings.preferredWebexSite}/wbxappapi/v2/meetings/spaceInstant`
198
233
  : '';
199
234
 
200
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
201
-
202
235
  return this.webex.request({
203
236
  method: HTTP_VERBS.POST,
204
237
  uri,
205
238
  body,
206
239
  });
207
240
  })
241
+ .then((requestResult) => {
242
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
243
+
244
+ return requestResult;
245
+ })
208
246
  .catch((err) => {
247
+ this.handlePolicyError(err);
248
+
209
249
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_FAILURE, {
210
250
  reason: err.message,
211
251
  stack: err.stack,
@@ -222,7 +262,7 @@ export default class MeetingInfoV2 {
222
262
  * @param {Object} captchaInfo
223
263
  * @param {String} captchaInfo.code
224
264
  * @param {String} captchaInfo.id
225
- * @param {String} installedOrgID
265
+ * @param {String} installedOrgID org ID of user's machine
226
266
  * @param {String} locusId
227
267
  * @param {Object} extraParams
228
268
  * @param {Object} options
@@ -256,7 +296,7 @@ export default class MeetingInfoV2 {
256
296
  this.webex.config.meetings.experimental.enableAdhocMeetings &&
257
297
  this.webex.meetings.preferredWebexSite
258
298
  ) {
259
- return this.createAdhocSpaceMeeting(destinationType.destination);
299
+ return this.createAdhocSpaceMeeting(destinationType.destination, installedOrgID);
260
300
  }
261
301
 
262
302
  const body = await MeetingInfoUtil.getRequestBody({
@@ -318,17 +358,7 @@ export default class MeetingInfoV2 {
318
358
  }
319
359
 
320
360
  if (err?.statusCode === 403) {
321
- if (POLICY_ERROR_CODES.includes(err.body?.code)) {
322
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_INFO_POLICY_ERROR, {
323
- code: err.body?.code,
324
- });
325
-
326
- throw new MeetingInfoV2PolicyError(
327
- err.body?.code,
328
- err.body?.data?.meetingInfo,
329
- err.body?.message
330
- );
331
- }
361
+ this.handlePolicyError(err);
332
362
 
333
363
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_PASSWORD_ERROR, {
334
364
  reason: err.message,
@@ -221,6 +221,7 @@ MeetingInfoUtil.getDestinationType = async (from) => {
221
221
  * Helper function to build up a correct locus url depending on the value passed
222
222
  * @param {Object} options type and value to fetch meeting info
223
223
  * @param {String} options.type One of [SIP_URI, PERSONAL_ROOM, MEETING_ID, CONVERSATION_URL, LOCUS_ID, MEETING_LINK]
224
+ * @param {String} options.installedOrgID org ID of user's machine
224
225
  * @param {Object} options.destination ?? value.value
225
226
  * @returns {Object} returns an object with {resource, method}
226
227
  */
@@ -226,6 +226,25 @@ describe('plugin-meetings', () => {
226
226
 
227
227
  });
228
228
 
229
+ it('adds deviceCapabilities and locale to request when they are provided', async () => {
230
+ await meetingsRequest.joinMeeting({
231
+ locale: 'en_UK',
232
+ deviceCapabilities: ['SERVER_AUDIO_ANNOUNCEMENT_SUPPORTED']
233
+ });
234
+ const requestParams = meetingsRequest.request.getCall(0).args[0];
235
+
236
+ assert.deepEqual(requestParams.body.deviceCapabilities, ['SERVER_AUDIO_ANNOUNCEMENT_SUPPORTED']);
237
+ assert.deepEqual(requestParams.body.locale, 'en_UK');
238
+ });
239
+
240
+ it('does not add deviceCapabilities and locale to request when they are not provided', async () => {
241
+ await meetingsRequest.joinMeeting({});
242
+ const requestParams = meetingsRequest.request.getCall(0).args[0];
243
+
244
+ assert.deepEqual(requestParams.body.deviceCapabilities, undefined);
245
+ assert.deepEqual(requestParams.body.locale, undefined);
246
+ });
247
+
229
248
  it('includes joinCookie correctly', async () => {
230
249
  const locusUrl = 'locusURL';
231
250
  const deviceUrl = 'deviceUrl';
@@ -193,6 +193,26 @@ describe('plugin-meetings', () => {
193
193
  assert.equal(parameter.breakoutsSupported, true);
194
194
  });
195
195
 
196
+ it('#Should call meetingRequest.joinMeeting with locale=en_UK, deviceCapabilities=["TEST"] when they are passed in as those values', async () => {
197
+ const meeting = {
198
+ meetingRequest: {
199
+ joinMeeting: sinon.stub().returns(Promise.resolve({body: {}, headers: {}})),
200
+ },
201
+ };
202
+
203
+ MeetingUtil.parseLocusJoin = sinon.stub();
204
+ await MeetingUtil.joinMeeting(meeting, {
205
+ locale: 'en_UK',
206
+ deviceCapabilities: ['TEST'],
207
+ });
208
+
209
+ assert.calledOnce(meeting.meetingRequest.joinMeeting);
210
+ const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
211
+
212
+ assert.equal(parameter.locale, 'en_UK');
213
+ assert.deepEqual(parameter.deviceCapabilities, ['TEST']);
214
+ });
215
+
196
216
  it('#Should call meetingRequest.joinMeeting with preferTranscoding=false when multistream is enabled', async () => {
197
217
  const meeting = {
198
218
  isMultistream: true,
@@ -304,6 +304,29 @@ describe('plugin-meetings', () => {
304
304
  meetingInfo.createAdhocSpaceMeeting.restore();
305
305
  });
306
306
 
307
+ it('create adhoc meeting when conversationUrl and installedOrgID passed with enableAdhocMeetings toggle', async () => {
308
+ sinon.stub(meetingInfo, 'createAdhocSpaceMeeting').returns(Promise.resolve());
309
+
310
+ const installedOrgID = '12345'
311
+
312
+ await meetingInfo.fetchMeetingInfo(
313
+ 'conversationUrl',
314
+ _CONVERSATION_URL_,
315
+ null,
316
+ null,
317
+ installedOrgID
318
+ );
319
+
320
+ assert.calledOnceWithExactly(
321
+ meetingInfo.createAdhocSpaceMeeting,
322
+ 'conversationUrl',
323
+ installedOrgID
324
+ );
325
+ assert.notCalled(webex.request);
326
+ meetingInfo.createAdhocSpaceMeeting.restore();
327
+ });
328
+
329
+
307
330
  it('should not call createAdhocSpaceMeeting if enableAdhocMeetings toggle is off', async () => {
308
331
  webex.config.meetings.experimental.enableAdhocMeetings = false;
309
332
  sinon.stub(meetingInfo, 'createAdhocSpaceMeeting').returns(Promise.resolve());
@@ -329,7 +352,7 @@ describe('plugin-meetings', () => {
329
352
  it('should throw an error MeetingInfoV2AdhocMeetingError if not able to start adhoc meeting for a conversation', async () => {
330
353
  webex.config.meetings.experimental.enableAdhocMeetings = true;
331
354
 
332
- webex.request = sinon.stub().rejects({statusCode: 403, body: {code: 400000}});
355
+ webex.request = sinon.stub().rejects({stack: 'a stack', message: 'a message', statusCode: 403, body: {code: 400000}});
333
356
  try {
334
357
  await meetingInfo.createAdhocSpaceMeeting('conversationUrl');
335
358
  } catch (err) {
@@ -339,6 +362,12 @@ describe('plugin-meetings', () => {
339
362
  'Failed starting the adhoc meeting, Please contact support team , code=400000'
340
363
  );
341
364
  assert.equal(err.wbxAppApiCode, 400000);
365
+ assert(Metrics.sendBehavioralMetric.calledOnce);
366
+ assert.calledWith(
367
+ Metrics.sendBehavioralMetric,
368
+ BEHAVIORAL_METRICS.ADHOC_MEETING_FAILURE,
369
+ {reason: 'a message', stack: 'a stack'}
370
+ );
342
371
  }
343
372
  });
344
373
 
@@ -563,8 +592,10 @@ describe('plugin-meetings', () => {
563
592
  });
564
593
 
565
594
  describe('createAdhocSpaceMeeting', () => {
566
- it('Make a request to /instantSpace when conversationUrl', async () => {
567
- const conversationUrl = 'https://conversationUrl/xxx';
595
+ const conversationUrl = 'https://conversationUrl/xxx';
596
+ const installedOrgID = '12345';
597
+
598
+ const setup = () => {
568
599
  const invitee = [];
569
600
 
570
601
  invitee.push({
@@ -577,7 +608,13 @@ describe('plugin-meetings', () => {
577
608
  ciUserUuid: conversation.participants.items[1].entryUUID,
578
609
  });
579
610
 
580
- await meetingInfo.createAdhocSpaceMeeting(conversationUrl);
611
+ return {invitee}
612
+ }
613
+
614
+ it('Make a request to /spaceInstant when conversationUrl', async () => {
615
+ const {invitee} = setup();
616
+
617
+ const result = await meetingInfo.createAdhocSpaceMeeting(conversationUrl);
581
618
 
582
619
  assert.calledWith(
583
620
  webex.internal.conversation.get,
@@ -598,7 +635,78 @@ describe('plugin-meetings', () => {
598
635
  });
599
636
  assert(Metrics.sendBehavioralMetric.calledOnce);
600
637
  assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
638
+ assert.deepEqual(result, {
639
+ body: {},
640
+ statusCode: 200
641
+ });
601
642
  });
643
+
644
+ it('Make a request to /spaceInstant when conversationUrl with installed org ID', async () => {
645
+ const {invitee} = setup();
646
+
647
+ await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID);
648
+
649
+ assert.calledWith(
650
+ webex.internal.conversation.get,
651
+ {url: conversationUrl},
652
+ {includeParticipants: true, disableTransform: true}
653
+ );
654
+
655
+ assert.calledWith(webex.request, {
656
+ method: 'POST',
657
+ uri: 'https://go.webex.com/wbxappapi/v2/meetings/spaceInstant',
658
+ body: {
659
+ title: conversation.displayName,
660
+ spaceUrl: conversation.url,
661
+ keyUrl: conversation.encryptionKeyUrl,
662
+ kroUrl: conversation.kmsResourceObjectUrl,
663
+ invitees: invitee,
664
+ installedOrgID,
665
+ },
666
+ });
667
+ assert(Metrics.sendBehavioralMetric.calledOnce);
668
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
669
+ });
670
+
671
+
672
+ forEach(
673
+ [
674
+ {errorCode: 403049},
675
+ {errorCode: 403104},
676
+ {errorCode: 403103},
677
+ {errorCode: 403048},
678
+ {errorCode: 403102},
679
+ {errorCode: 403101},
680
+ ],
681
+ ({errorCode}) => {
682
+ it(`should throw a MeetingInfoV2PolicyError for error code ${errorCode}`, async () => {
683
+ const message = 'a message';
684
+ const meetingInfoData = 'meeting info';
685
+
686
+ webex.request = sinon.stub().rejects({
687
+ statusCode: 403,
688
+ body: {message, code: errorCode, data: {meetingInfo: meetingInfoData}},
689
+ });
690
+ try {
691
+ await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID);
692
+ assert.fail('createAdhocSpaceMeeting should have thrown, but has not done that');
693
+ } catch (err) {
694
+ assert.instanceOf(err, MeetingInfoV2PolicyError);
695
+ assert.deepEqual(err.message, `${message}, code=${errorCode}`);
696
+ assert.equal(err.wbxAppApiCode, errorCode);
697
+ assert.deepEqual(err.meetingInfo, meetingInfoData);
698
+
699
+ assert(Metrics.sendBehavioralMetric.calledOnce);
700
+ assert.calledWith(
701
+ Metrics.sendBehavioralMetric,
702
+ BEHAVIORAL_METRICS.MEETING_INFO_POLICY_ERROR,
703
+ {code: errorCode}
704
+ );
705
+
706
+ }
707
+ });
708
+ }
709
+ );
602
710
  });
603
711
  });
604
712
  });