@webex/plugin-meetings 1.149.1 → 1.151.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.
Files changed (42) hide show
  1. package/dist/constants.js +14 -4
  2. package/dist/constants.js.map +1 -1
  3. package/dist/locus-info/mediaSharesUtils.js +88 -11
  4. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  5. package/dist/meeting/index.js +368 -42
  6. package/dist/meeting/index.js.map +1 -1
  7. package/dist/meeting/request.js +17 -41
  8. package/dist/meeting/request.js.map +1 -1
  9. package/dist/meeting-info/index.js +1 -1
  10. package/dist/meeting-info/index.js.map +1 -1
  11. package/dist/meeting-info/util.js +14 -0
  12. package/dist/meeting-info/util.js.map +1 -1
  13. package/dist/meetings/index.js +1 -1
  14. package/dist/meetings/index.js.map +1 -1
  15. package/dist/meetings/util.js +0 -16
  16. package/dist/meetings/util.js.map +1 -1
  17. package/dist/members/index.js +72 -11
  18. package/dist/members/index.js.map +1 -1
  19. package/dist/metrics/config.js +6 -0
  20. package/dist/metrics/config.js.map +1 -1
  21. package/dist/metrics/index.js +26 -1
  22. package/dist/metrics/index.js.map +1 -1
  23. package/dist/peer-connection-manager/index.js +1 -1
  24. package/dist/peer-connection-manager/index.js.map +1 -1
  25. package/package.json +5 -5
  26. package/src/constants.js +11 -2
  27. package/src/locus-info/mediaSharesUtils.js +84 -12
  28. package/src/meeting/index.js +350 -29
  29. package/src/meeting/request.js +50 -29
  30. package/src/meeting-info/index.js +4 -1
  31. package/src/meeting-info/util.js +13 -0
  32. package/src/meetings/index.js +1 -1
  33. package/src/meetings/util.js +0 -14
  34. package/src/members/index.js +80 -11
  35. package/src/metrics/config.js +6 -0
  36. package/src/metrics/index.js +23 -2
  37. package/src/peer-connection-manager/index.js +7 -2
  38. package/test/integration/spec/journey.js +177 -3
  39. package/test/integration/spec/space-meeting.js +33 -5
  40. package/test/unit/spec/meeting/index.js +547 -2
  41. package/test/utils/testUtils.js +18 -1
  42. package/test/utils/webex-test-users.js +2 -0
@@ -51,6 +51,7 @@ import {
51
51
  MEETING_STATE_MACHINE,
52
52
  MEETING_STATE,
53
53
  MEETINGS,
54
+ METRICS_JOIN_TIMES_MAX_DURATION,
54
55
  METRICS_OPERATIONAL_MEASURES,
55
56
  MQA_STATS,
56
57
  NETWORK_STATUS,
@@ -1065,10 +1066,45 @@ export default class Meeting extends StatelessWebexPlugin {
1065
1066
  };
1066
1067
  }
1067
1068
 
1069
+ const localSDPGenRemoteSDPRecv = this.getLocalSDPGenRemoteSDPRecvDelay();
1070
+
1071
+ if (localSDPGenRemoteSDPRecv) {
1072
+ options.joinTimes = {
1073
+ ...options.joinTimes,
1074
+ localSDPGenRemoteSDPRecv
1075
+ };
1076
+ }
1077
+
1078
+ const callInitiateJoinReq = this.getCallInitiateJoinReq();
1079
+
1080
+ if (callInitiateJoinReq) {
1081
+ options.joinTimes = {
1082
+ ...options.joinTimes,
1083
+ callInitiateJoinReq
1084
+ };
1085
+ }
1086
+
1087
+ const joinReqResp = this.getJoinReqResp();
1088
+
1089
+ if (joinReqResp) {
1090
+ options.joinTimes = {
1091
+ ...options.joinTimes,
1092
+ joinReqResp
1093
+ };
1094
+ }
1095
+
1096
+ const getTotalJmt = this.getTotalJmt();
1097
+
1098
+ if (getTotalJmt) {
1099
+ options.joinTimes = {
1100
+ ...options.joinTimes,
1101
+ getTotalJmt
1102
+ };
1103
+ }
1104
+
1068
1105
  if (options.type === MQA_STATS.CA_TYPE) {
1069
1106
  payload = Metrics.initMediaPayload(options.event, identifiers, options);
1070
1107
  }
1071
-
1072
1108
  else {
1073
1109
  payload = Metrics.initPayload(options.event, identifiers, options);
1074
1110
  }
@@ -1310,7 +1346,7 @@ export default class Meeting extends StatelessWebexPlugin {
1310
1346
 
1311
1347
  /**
1312
1348
  * Set up the locus info media shares listener
1313
- * update content sharing id value for members, and updates the member
1349
+ * update content and whiteboard sharing id value for members, and updates the member
1314
1350
  * notifies consumer with members:content:update {activeContentSharingId, endedContentSharingId}
1315
1351
  * @returns {undefined}
1316
1352
  * @private
@@ -1319,11 +1355,16 @@ export default class Meeting extends StatelessWebexPlugin {
1319
1355
  setUpLocusMediaSharesListener() {
1320
1356
  // Will get triggered on local and remote share
1321
1357
  this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES, (payload) => {
1322
- const {contentId, disposition} = payload.current;
1358
+ const {content: contentShare, whiteboard: whiteboardShare} = payload.current;
1359
+ const previousContentShare = payload.previous?.content;
1360
+ const previousWhiteboardShare = payload.previous?.whiteboard;
1323
1361
 
1324
1362
  if (
1325
- contentId === payload.previous?.contentId &&
1326
- disposition === payload.previous?.disposition
1363
+ (contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
1364
+ contentShare.disposition === previousContentShare?.disposition) &&
1365
+ (whiteboardShare.beneficiaryId === previousWhiteboardShare?.beneficiaryId &&
1366
+ whiteboardShare.disposition === previousWhiteboardShare?.disposition &&
1367
+ whiteboardShare.resourceUrl === previousWhiteboardShare?.resourceUrl)
1327
1368
  ) {
1328
1369
  // nothing changed, so ignore
1329
1370
  // (this happens when we steal presentation from remote)
@@ -1334,15 +1375,16 @@ export default class Meeting extends StatelessWebexPlugin {
1334
1375
 
1335
1376
  // REMOTE - check if remote started sharing
1336
1377
  if (
1337
- this.selfId !== contentId &&
1338
- disposition === FLOOR_ACTION.GRANTED
1378
+ this.selfId !== contentShare.beneficiaryId &&
1379
+ contentShare.disposition === FLOOR_ACTION.GRANTED
1339
1380
  ) {
1381
+ // CONTENT - sharing content remote
1340
1382
  newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
1341
1383
  }
1342
- // LOCAL - check if we started sharing
1384
+ // LOCAL - check if we started sharing content
1343
1385
  else if (
1344
- this.selfId === contentId &&
1345
- disposition === FLOOR_ACTION.GRANTED
1386
+ this.selfId === contentShare.beneficiaryId &&
1387
+ contentShare.disposition === FLOOR_ACTION.GRANTED
1346
1388
  ) {
1347
1389
  if (this.mediaProperties.shareTrack?.readyState === 'ended') {
1348
1390
  this.stopShare({
@@ -1353,13 +1395,23 @@ export default class Meeting extends StatelessWebexPlugin {
1353
1395
  });
1354
1396
  }
1355
1397
  else {
1398
+ // CONTENT - sharing content local
1356
1399
  newShareStatus = SHARE_STATUS.LOCAL_SHARE_ACTIVE;
1357
1400
  }
1358
1401
  }
1359
- // or if sharing has been stopped
1402
+ // If we did not hit the cases above, no one is sharng content, so we check if we are sharing whiteboard
1403
+ // There is no concept of local/remote share for whiteboard
1404
+ // It does not matter who requested to share the whiteboard, everyone gets the same view
1405
+ else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
1406
+ // WHITEBOARD - sharing whiteboard
1407
+ newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
1408
+ }
1409
+ // or if content share is either released or null and whiteboard share is either released or null, no one is sharing
1360
1410
  else if (
1361
- payload.previous &&
1362
- disposition === FLOOR_ACTION.RELEASED
1411
+ (previousContentShare &&
1412
+ (contentShare.disposition === FLOOR_ACTION.RELEASED) || (contentShare.disposition === null)) &&
1413
+ (previousWhiteboardShare &&
1414
+ (whiteboardShare.disposition === FLOOR_ACTION.RELEASED) || (whiteboardShare.disposition === null))
1363
1415
  ) {
1364
1416
  newShareStatus = SHARE_STATUS.NO_SHARE;
1365
1417
  }
@@ -1397,6 +1449,17 @@ export default class Meeting extends StatelessWebexPlugin {
1397
1449
  );
1398
1450
  break;
1399
1451
 
1452
+ case SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE:
1453
+ Trigger.trigger(
1454
+ this,
1455
+ {
1456
+ file: 'meeting/index',
1457
+ function: 'stopWhiteboardShare'
1458
+ },
1459
+ EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD
1460
+ );
1461
+ break;
1462
+
1400
1463
  case SHARE_STATUS.NO_SHARE:
1401
1464
  // nothing to do
1402
1465
  break;
@@ -1417,13 +1480,16 @@ export default class Meeting extends StatelessWebexPlugin {
1417
1480
  },
1418
1481
  EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
1419
1482
  {
1420
- memberId: contentId
1483
+ memberId: contentShare.beneficiaryId
1421
1484
  }
1422
1485
  );
1423
1486
  };
1424
1487
 
1425
1488
  // if a remote participant is stealing the presentation from us
1426
- if (this.mediaProperties.mediaDirection?.sendShare) {
1489
+ if (!this.mediaProperties.mediaDirection?.sendShare || oldShareStatus === SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE) {
1490
+ sendStartedSharingRemote();
1491
+ }
1492
+ else {
1427
1493
  this.updateShare({
1428
1494
  sendShare: false,
1429
1495
  receiveShare: this.mediaProperties.mediaDirection.receiveShare
@@ -1432,9 +1498,6 @@ export default class Meeting extends StatelessWebexPlugin {
1432
1498
  sendStartedSharingRemote();
1433
1499
  });
1434
1500
  }
1435
- else {
1436
- sendStartedSharingRemote();
1437
- }
1438
1501
  break;
1439
1502
  }
1440
1503
 
@@ -1450,6 +1513,22 @@ export default class Meeting extends StatelessWebexPlugin {
1450
1513
  Metrics.postEvent({event: eventType.LOCAL_SHARE_FLOOR_GRANTED, meeting: this});
1451
1514
  break;
1452
1515
 
1516
+ case SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE:
1517
+ Trigger.trigger(
1518
+ this,
1519
+ {
1520
+ file: 'meeting/index',
1521
+ function: 'startWhiteboardShare'
1522
+ },
1523
+ EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
1524
+ {
1525
+ resourceUrl: whiteboardShare.resourceUrl,
1526
+ memberId: whiteboardShare.beneficiaryId
1527
+ }
1528
+ );
1529
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_FLOOR_GRANTED, meeting: this});
1530
+ break;
1531
+
1453
1532
  case SHARE_STATUS.NO_SHARE:
1454
1533
  // nothing to do
1455
1534
  break;
@@ -1471,9 +1550,27 @@ export default class Meeting extends StatelessWebexPlugin {
1471
1550
  },
1472
1551
  EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
1473
1552
  {
1474
- memberId: contentId
1553
+ memberId: contentShare.beneficiaryId
1554
+ }
1555
+ );
1556
+ this.members.locusMediaSharesUpdate(payload);
1557
+ }
1558
+ else if (newShareStatus === SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE) {
1559
+ // if we got here, then some remote participant has stolen
1560
+ // the presentation from another remote participant
1561
+ Trigger.trigger(
1562
+ this,
1563
+ {
1564
+ file: 'meeting/index',
1565
+ function: 'startWhiteboardShare'
1566
+ },
1567
+ EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
1568
+ {
1569
+ resourceUrl: whiteboardShare.resourceUrl,
1570
+ memberId: whiteboardShare.beneficiaryId
1475
1571
  }
1476
1572
  );
1573
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_FLOOR_GRANTED, meeting: this});
1477
1574
  this.members.locusMediaSharesUpdate(payload);
1478
1575
  }
1479
1576
  });
@@ -3311,12 +3408,13 @@ export default class Meeting extends StatelessWebexPlugin {
3311
3408
  }
3312
3409
  }
3313
3410
 
3314
- return MeetingUtil.joinMeetingOptions(this, options).then((join) => {
3315
- this.meetingFiniteStateMachine.join();
3316
- LoggerProxy.logger.log('Meeting:index#join --> Success');
3411
+ return MeetingUtil.joinMeetingOptions(this, options)
3412
+ .then((join) => {
3413
+ this.meetingFiniteStateMachine.join();
3414
+ LoggerProxy.logger.log('Meeting:index#join --> Success');
3317
3415
 
3318
- return join;
3319
- })
3416
+ return join;
3417
+ })
3320
3418
  .then((join) => {
3321
3419
  joinSuccess(join);
3322
3420
  this.deferJoin = undefined;
@@ -3790,7 +3888,8 @@ export default class Meeting extends StatelessWebexPlugin {
3790
3888
  meetingId: this.id,
3791
3889
  remoteQualityLevel: this.mediaProperties.remoteQualityLevel,
3792
3890
  enableRtx: this.config.enableRtx,
3793
- enableExtmap: this.config.enableExtmap
3891
+ enableExtmap: this.config.enableExtmap,
3892
+ setStartLocalSDPGenRemoteSDPRecvDelay: this.setStartLocalSDPGenRemoteSDPRecvDelay.bind(this)
3794
3893
  })
3795
3894
  .then((peerConnection) => this.getDevices().then((devices) => {
3796
3895
  MeetingUtil.handleDeviceLogging(devices);
@@ -4491,6 +4590,105 @@ export default class Meeting extends StatelessWebexPlugin {
4491
4590
  });
4492
4591
  }
4493
4592
 
4593
+ /**
4594
+ * Start sharing whiteboard given channelUrl
4595
+ * @param {string} channelUrl whiteboard url
4596
+ * @param {String} resourceToken token created by authorize media injector
4597
+ * @returns {Promise}
4598
+ * @public
4599
+ * @memberof Meeting
4600
+ */
4601
+ startWhiteboardShare(channelUrl, resourceToken) {
4602
+ const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
4603
+
4604
+ if (!channelUrl) {
4605
+ return Promise.reject(new ParameterError('Cannot share without channelUrl.'));
4606
+ }
4607
+
4608
+ if (whiteboard) {
4609
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_INITIATED, meeting: this});
4610
+
4611
+ const body = {
4612
+ disposition: FLOOR_ACTION.GRANTED,
4613
+ personUrl: this.locusInfo.self.url,
4614
+ deviceUrl: this.deviceUrl,
4615
+ uri: whiteboard.url,
4616
+ resourceUrl: channelUrl
4617
+ };
4618
+
4619
+ if (resourceToken) {
4620
+ body.resourceToken = resourceToken;
4621
+ }
4622
+
4623
+ return this.meetingRequest.changeMeetingFloor(body)
4624
+ .then(() => {
4625
+ this.isSharing = false;
4626
+
4627
+ return Promise.resolve();
4628
+ })
4629
+ .catch((error) => {
4630
+ LoggerProxy.logger.error('Meeting:index#startWhiteboardShare --> Error ', error);
4631
+
4632
+ Metrics.sendOperationalMetric(
4633
+ METRICS_OPERATIONAL_MEASURES.MEETING_START_WHITEBOARD_SHARE_FAILURE,
4634
+ {
4635
+ correlation_id: this.correlationId,
4636
+ locus_id: this.locusUrl.split('/').pop(),
4637
+ reason: error.message,
4638
+ stack: error.stack,
4639
+ board: {channelUrl}
4640
+ }
4641
+ );
4642
+
4643
+ return Promise.reject(error);
4644
+ });
4645
+ }
4646
+
4647
+ return Promise.reject(new ParameterError('Cannot share without whiteboard.'));
4648
+ }
4649
+
4650
+ /**
4651
+ * Stop sharing whiteboard given channelUrl
4652
+ * @param {string} channelUrl whiteboard url
4653
+ * @returns {Promise}
4654
+ * @public
4655
+ * @memberof Meeting
4656
+ */
4657
+ stopWhiteboardShare(channelUrl) {
4658
+ const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
4659
+
4660
+ if (whiteboard) {
4661
+ Metrics.postEvent({event: eventType.WHITEBOARD_SHARE_STOPPED, meeting: this});
4662
+
4663
+ return this.meetingRequest.changeMeetingFloor({
4664
+ disposition: FLOOR_ACTION.RELEASED,
4665
+ personUrl: this.locusInfo.self.url,
4666
+ deviceUrl: this.deviceUrl,
4667
+ uri: whiteboard.url
4668
+ })
4669
+ .catch((error) => {
4670
+ LoggerProxy.logger.error('Meeting:index#stopWhiteboardShare --> Error ', error);
4671
+
4672
+ Metrics.sendOperationalMetric(
4673
+ METRICS_OPERATIONAL_MEASURES.STOP_WHITEBOARD_SHARE_FAILURE,
4674
+ {
4675
+ correlation_id: this.correlationId,
4676
+ locus_id: this.locusUrl.split('/').pop(),
4677
+ reason: error.message,
4678
+ stack: error.stack,
4679
+ board: {channelUrl}
4680
+ }
4681
+ );
4682
+
4683
+ return Promise.reject(error);
4684
+ })
4685
+ .finally(() => {
4686
+ });
4687
+ }
4688
+
4689
+ return Promise.reject(new ParameterError('Cannot stop share without whiteboard.'));
4690
+ }
4691
+
4494
4692
  /**
4495
4693
  * Start sharing content with server
4496
4694
  * @returns {Promise} see #meetingRequest.changeMeetingFloor
@@ -4564,7 +4762,7 @@ export default class Meeting extends StatelessWebexPlugin {
4564
4762
  Metrics.postEvent({event: eventType.SHARE_STOPPED, meeting: this});
4565
4763
  Media.stopTracks(this.mediaProperties.shareTrack);
4566
4764
 
4567
- if (this.contentId !== this.selfId) {
4765
+ if (content.floor.beneficiary.id !== this.selfId) {
4568
4766
  // remote participant started sharing and caused our sharing to stop, we don't want to send any floor action request in that case
4569
4767
  this.isSharing = false;
4570
4768
 
@@ -5078,9 +5276,9 @@ export default class Meeting extends StatelessWebexPlugin {
5078
5276
  }
5079
5277
 
5080
5278
  /**
5081
- * @param {string} typeMedia 'audio' or 'video'
5082
- * @returns {undefined}
5083
- */
5279
+ * @param {string} typeMedia 'audio' or 'video'
5280
+ * @returns {undefined}
5281
+ */
5084
5282
  setEndSendingMediaDelay(typeMedia) {
5085
5283
  this[`endSendingMediaDelay${typeMedia}`] = performance.now();
5086
5284
  }
@@ -5095,4 +5293,127 @@ export default class Meeting extends StatelessWebexPlugin {
5095
5293
 
5096
5294
  return (start && end) ? end - start : undefined;
5097
5295
  }
5296
+
5297
+ /**
5298
+ *
5299
+ * @returns {undefined}
5300
+ */
5301
+ setStartLocalSDPGenRemoteSDPRecvDelay() {
5302
+ if (!this.startLocalSDPGenRemoteSDPRecvDelay) {
5303
+ this.startLocalSDPGenRemoteSDPRecvDelay = performance.now();
5304
+ this.endLocalSDPGenRemoteSDPRecvDelay = undefined;
5305
+ }
5306
+ }
5307
+
5308
+ /**
5309
+ *
5310
+ * @returns {undefined}
5311
+ */
5312
+ setEndLocalSDPGenRemoteSDPRecvDelay() {
5313
+ if (!this.endLocalSDPGenRemoteSDPRecvDelay) {
5314
+ this.endLocalSDPGenRemoteSDPRecvDelay = performance.now();
5315
+ }
5316
+ }
5317
+
5318
+ /**
5319
+ *
5320
+ * @returns {string} duration between local SDP generation and remote SDP reception
5321
+ */
5322
+ getLocalSDPGenRemoteSDPRecvDelay() {
5323
+ const start = this.startLocalSDPGenRemoteSDPRecvDelay;
5324
+ const end = this.endLocalSDPGenRemoteSDPRecvDelay;
5325
+
5326
+ if (start && end) {
5327
+ const calculatedDelay = end - start;
5328
+
5329
+ return calculatedDelay > METRICS_JOIN_TIMES_MAX_DURATION ?
5330
+ undefined :
5331
+ calculatedDelay;
5332
+ }
5333
+
5334
+ return undefined;
5335
+ }
5336
+
5337
+ /**
5338
+ *
5339
+ * @returns {undefined}
5340
+ */
5341
+ setStartCallInitiateJoinReq() {
5342
+ this.startCallInitiateJoinReq = performance.now();
5343
+ this.endCallInitiateJoinReq = undefined;
5344
+ }
5345
+
5346
+ /**
5347
+ *
5348
+ * @returns {undefined}
5349
+ */
5350
+ setEndCallInitiateJoinReq() {
5351
+ this.endCallInitiateJoinReq = performance.now();
5352
+ }
5353
+
5354
+ /**
5355
+ *
5356
+ * @returns {string} duration between call initiate and sending join request to locus
5357
+ */
5358
+ getCallInitiateJoinReq() {
5359
+ const start = this.startCallInitiateJoinReq;
5360
+ const end = this.endCallInitiateJoinReq;
5361
+
5362
+ if (start && end) {
5363
+ const calculatedDelay = end - start;
5364
+
5365
+ return calculatedDelay > METRICS_JOIN_TIMES_MAX_DURATION ?
5366
+ undefined :
5367
+ calculatedDelay;
5368
+ }
5369
+
5370
+ return undefined;
5371
+ }
5372
+
5373
+ /**
5374
+ *
5375
+ * @returns {undefined}
5376
+ */
5377
+ setStartJoinReqResp() {
5378
+ this.startJoinReqResp = performance.now();
5379
+ this.endJoinReqResp = undefined;
5380
+ }
5381
+
5382
+ /**
5383
+ *
5384
+ * @returns {undefined}
5385
+ */
5386
+ setEndJoinReqResp() {
5387
+ this.endJoinReqResp = performance.now();
5388
+ }
5389
+
5390
+ /**
5391
+ *
5392
+ * @returns {string} duration between sending locus join request and receiving join response
5393
+ */
5394
+ getJoinReqResp() {
5395
+ const start = this.startJoinReqResp;
5396
+ const end = this.endJoinReqResp;
5397
+
5398
+ if (start && end) {
5399
+ const calculatedDelay = end - start;
5400
+
5401
+ return calculatedDelay > METRICS_JOIN_TIMES_MAX_DURATION ?
5402
+ undefined :
5403
+ calculatedDelay;
5404
+ }
5405
+
5406
+ return undefined;
5407
+ }
5408
+
5409
+ /**
5410
+ *
5411
+ * @returns {string} duration between call initiate and successful locus join (even if it is in lobby)
5412
+ */
5413
+ getTotalJmt() {
5414
+ const start = this.startCallInitiateJoinReq;
5415
+ const end = this.endJoinReqResp;
5416
+
5417
+ return (start && end) ? end - start : undefined;
5418
+ }
5098
5419
  }
@@ -1,9 +1,7 @@
1
1
  import uuid from 'uuid';
2
2
  import {debounce} from 'lodash';
3
3
  import {StatelessWebexPlugin} from '@webex/webex-core';
4
- import {
5
- deviceType
6
- } from '@webex/common';
4
+ import {deviceType} from '@webex/common';
7
5
 
8
6
  import LoggerProxy from '../common/logs/logger-proxy';
9
7
  import {
@@ -51,7 +49,19 @@ export default class MeetingRequest extends StatelessWebexPlugin {
51
49
  */
52
50
  async joinMeeting(options) {
53
51
  const {
54
- asResourceOccupant, sipUri, meetingNumber, deviceUrl, locusUrl, resourceId, correlationId, ensureConversation, moderator, pin, moveToResource, roapMessage, preferTranscoding
52
+ asResourceOccupant,
53
+ sipUri,
54
+ meetingNumber,
55
+ deviceUrl,
56
+ locusUrl,
57
+ resourceId,
58
+ correlationId,
59
+ ensureConversation,
60
+ moderator,
61
+ pin,
62
+ moveToResource,
63
+ roapMessage,
64
+ preferTranscoding
55
65
  } = options;
56
66
 
57
67
  LoggerProxy.logger.info(
@@ -95,29 +105,16 @@ export default class MeetingRequest extends StatelessWebexPlugin {
95
105
  if (locusUrl) {
96
106
  url = `${locusUrl}/${PARTICIPANT}`;
97
107
  }
98
- else if (meetingNumber) {
108
+ else if (sipUri || meetingNumber) {
99
109
  try {
100
110
  await this.webex.internal.services.waitForCatalog('postauth');
101
111
  url = `${this.webex.internal.services.get('locus')}/${LOCI}/${CALL}`;
102
112
  body.invitee = {
103
- address: `wbxmn:${meetingNumber}`
113
+ address: sipUri || `wbxmn:${meetingNumber}`
104
114
  };
105
115
  }
106
116
  catch (e) {
107
- LoggerProxy.logger.error(`Meeting:request#joinMeeting webex meeting id--> ${e}`);
108
- throw (e);
109
- }
110
- }
111
- else if (sipUri) {
112
- try {
113
- await this.webex.internal.services.waitForCatalog('postauth');
114
- url = `${this.webex.internal.services.get('locus')}/${LOCI}/${CALL}`;
115
- body.invitee = {
116
- address: sipUri
117
- };
118
- }
119
- catch (e) {
120
- LoggerProxy.logger.error(`Meeting:request#joinMeeting sipUrl --> ${e}`);
117
+ LoggerProxy.logger.error(`Meeting:request#joinMeeting ${sipUri ? 'sipUri' : 'meetingNumber'} --> ${e}`);
121
118
  throw (e);
122
119
  }
123
120
  }
@@ -154,7 +151,10 @@ export default class MeetingRequest extends StatelessWebexPlugin {
154
151
  * @private
155
152
  */
156
153
  dialIn({
157
- locusUrl, dialInUrl, clientUrl, correlationId
154
+ locusUrl,
155
+ dialInUrl,
156
+ clientUrl,
157
+ correlationId
158
158
  }) {
159
159
  LoggerProxy.logger.info(
160
160
  'Meeting:request#dialIn --> Provisioning a dial in device',
@@ -195,7 +195,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
195
195
  * @private
196
196
  */
197
197
  dialOut({
198
- locusUrl, dialOutUrl, phoneNumber, clientUrl, correlationId
198
+ locusUrl,
199
+ dialOutUrl,
200
+ phoneNumber,
201
+ clientUrl,
202
+ correlationId
199
203
  }) {
200
204
  LoggerProxy.logger.info(
201
205
  'Meeting:request#dialOut --> Provisioning a dial out device',
@@ -294,7 +298,10 @@ export default class MeetingRequest extends StatelessWebexPlugin {
294
298
  * @private
295
299
  */
296
300
  disconnectPhoneAudio({
297
- locusUrl, phoneUrl, correlationId, selfId
301
+ locusUrl,
302
+ phoneUrl,
303
+ correlationId,
304
+ selfId
298
305
  }) {
299
306
  LoggerProxy.logger.info(
300
307
  `Meeting:request#disconnectPhoneAudio --> request phone ${phoneUrl} to leave`,
@@ -334,7 +341,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
334
341
  * @returns {Promise}
335
342
  */
336
343
  leaveMeeting({
337
- locusUrl, selfId, deviceUrl: url, resourceId, correlationId
344
+ locusUrl,
345
+ selfId,
346
+ deviceUrl: url,
347
+ resourceId,
348
+ correlationId
338
349
  }) {
339
350
  LoggerProxy.logger.info(
340
351
  'Meeting:request#leaveMeeting --> Leaving a meeting',
@@ -512,13 +523,19 @@ export default class MeetingRequest extends StatelessWebexPlugin {
512
523
  };
513
524
  }
514
525
 
526
+ const body = {
527
+ floor: floorReq,
528
+ resourceUrl: options.resourceUrl
529
+ };
530
+
531
+ if (options?.resourceToken) {
532
+ body.resourceToken = options?.resourceToken;
533
+ }
534
+
515
535
  return this.request({
516
536
  uri: options.uri,
517
537
  method: HTTP_VERBS.PUT,
518
- body: {
519
- floor: floorReq,
520
- resourceUrl: options.resourceUrl
521
- }
538
+ body
522
539
  });
523
540
  }
524
541
 
@@ -559,7 +576,11 @@ export default class MeetingRequest extends StatelessWebexPlugin {
559
576
  * @returns {Promise}
560
577
  */
561
578
  changeVideoLayout({
562
- locusUrl, deviceUrl, layoutType, main, content
579
+ locusUrl,
580
+ deviceUrl,
581
+ layoutType,
582
+ main,
583
+ content
563
584
  }) {
564
585
  // send main/content renderInfo only if both width and height are specified
565
586
  if (main && (!main.width || !main.height)) {
@@ -108,7 +108,10 @@ export default class MeetingInfo {
108
108
  * @memberof MeetingInfo
109
109
  */
110
110
  fetchMeetingInfo(destination, type = null) {
111
- return this.fetchInfoOptions(destination, type).then((options) =>
111
+ return this.fetchInfoOptions(
112
+ MeetingInfoUtil.extractDestination(destination, type),
113
+ type
114
+ ).then((options) =>
112
115
  // fetch meeting info
113
116
  this.requestFetchInfo(options).catch((error) => {
114
117
  // if it failed the first time as meeting link
@@ -35,6 +35,19 @@ import {
35
35
 
36
36
  const MeetingInfoUtil = {};
37
37
 
38
+ MeetingInfoUtil.extractDestination = (destination, type) => {
39
+ let dest = destination;
40
+
41
+ if (type === _LOCUS_ID_) {
42
+ if (!(destination && destination.url)) {
43
+ throw new ParameterError('You cannot create a meeting by locus without a locus.url defined');
44
+ }
45
+ dest = destination.url;
46
+ }
47
+
48
+ return dest;
49
+ };
50
+
38
51
  MeetingInfoUtil.getParsedUrl = (link) => {
39
52
  try {
40
53
  let parsedUrl = url.parse(link);
@@ -724,7 +724,7 @@ export default class Meetings extends WebexPlugin {
724
724
  this.meetingCollection.set(meeting);
725
725
 
726
726
  try {
727
- const info = await this.meetingInfo.fetchMeetingInfo(MeetingsUtil.extractDestination(destination, type), type);
727
+ const info = await this.meetingInfo.fetchMeetingInfo(destination, type);
728
728
 
729
729
  meeting.parseMeetingInfo(info);
730
730
  meeting.meetingInfo = info ? info.body : null;