@webex/plugin-meetings 3.10.0-next.9 → 3.10.0-webex-services-ready.2

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 (73) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/constants.js +11 -3
  4. package/dist/constants.js.map +1 -1
  5. package/dist/hashTree/constants.js +20 -0
  6. package/dist/hashTree/constants.js.map +1 -0
  7. package/dist/hashTree/hashTree.js +515 -0
  8. package/dist/hashTree/hashTree.js.map +1 -0
  9. package/dist/hashTree/hashTreeParser.js +1266 -0
  10. package/dist/hashTree/hashTreeParser.js.map +1 -0
  11. package/dist/hashTree/types.js +22 -0
  12. package/dist/hashTree/types.js.map +1 -0
  13. package/dist/hashTree/utils.js +48 -0
  14. package/dist/hashTree/utils.js.map +1 -0
  15. package/dist/interpretation/index.js +1 -1
  16. package/dist/interpretation/siLanguage.js +1 -1
  17. package/dist/locus-info/index.js +529 -75
  18. package/dist/locus-info/index.js.map +1 -1
  19. package/dist/locus-info/types.js +7 -0
  20. package/dist/locus-info/types.js.map +1 -0
  21. package/dist/meeting/index.js +63 -22
  22. package/dist/meeting/index.js.map +1 -1
  23. package/dist/meeting/util.js +1 -0
  24. package/dist/meeting/util.js.map +1 -1
  25. package/dist/meetings/index.js +112 -70
  26. package/dist/meetings/index.js.map +1 -1
  27. package/dist/metrics/constants.js +3 -1
  28. package/dist/metrics/constants.js.map +1 -1
  29. package/dist/reachability/clusterReachability.js +44 -358
  30. package/dist/reachability/clusterReachability.js.map +1 -1
  31. package/dist/reachability/reachability.types.js +14 -1
  32. package/dist/reachability/reachability.types.js.map +1 -1
  33. package/dist/reachability/reachabilityPeerConnection.js +445 -0
  34. package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
  35. package/dist/types/constants.d.ts +26 -21
  36. package/dist/types/hashTree/constants.d.ts +8 -0
  37. package/dist/types/hashTree/hashTree.d.ts +129 -0
  38. package/dist/types/hashTree/hashTreeParser.d.ts +260 -0
  39. package/dist/types/hashTree/types.d.ts +27 -0
  40. package/dist/types/hashTree/utils.d.ts +9 -0
  41. package/dist/types/locus-info/index.d.ts +95 -56
  42. package/dist/types/locus-info/types.d.ts +54 -0
  43. package/dist/types/meeting/index.d.ts +22 -9
  44. package/dist/types/meetings/index.d.ts +9 -2
  45. package/dist/types/metrics/constants.d.ts +2 -0
  46. package/dist/types/reachability/clusterReachability.d.ts +10 -88
  47. package/dist/types/reachability/reachability.types.d.ts +12 -1
  48. package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
  49. package/dist/webinar/index.js +1 -1
  50. package/package.json +22 -21
  51. package/src/constants.ts +13 -1
  52. package/src/hashTree/constants.ts +9 -0
  53. package/src/hashTree/hashTree.ts +463 -0
  54. package/src/hashTree/hashTreeParser.ts +1161 -0
  55. package/src/hashTree/types.ts +32 -0
  56. package/src/hashTree/utils.ts +42 -0
  57. package/src/locus-info/index.ts +571 -106
  58. package/src/locus-info/types.ts +53 -0
  59. package/src/meeting/index.ts +78 -27
  60. package/src/meeting/util.ts +1 -0
  61. package/src/meetings/index.ts +104 -51
  62. package/src/metrics/constants.ts +2 -0
  63. package/src/reachability/clusterReachability.ts +50 -347
  64. package/src/reachability/reachability.types.ts +15 -1
  65. package/src/reachability/reachabilityPeerConnection.ts +416 -0
  66. package/test/unit/spec/hashTree/hashTree.ts +655 -0
  67. package/test/unit/spec/hashTree/hashTreeParser.ts +1532 -0
  68. package/test/unit/spec/hashTree/utils.ts +103 -0
  69. package/test/unit/spec/locus-info/index.js +722 -4
  70. package/test/unit/spec/meeting/index.js +120 -20
  71. package/test/unit/spec/meeting/utils.js +77 -0
  72. package/test/unit/spec/meetings/index.js +71 -26
  73. package/test/unit/spec/reachability/clusterReachability.ts +281 -138
@@ -1003,6 +1003,35 @@ describe('plugin-meetings', () => {
1003
1003
  );
1004
1004
  });
1005
1005
 
1006
+ it('should call leave() if addMediaInternal() fails ', async () => {
1007
+ const addMediaError = new Error('fake addMedia error');
1008
+ addMediaError.name = 'TypeError';
1009
+
1010
+ const rejectError = {
1011
+ error: {
1012
+ body: {
1013
+ errorCode: 2729,
1014
+ message: 'fake addMedia error',
1015
+ name: 'TypeError'
1016
+ }
1017
+ }
1018
+ };
1019
+ meeting.addMediaInternal.rejects(addMediaError);
1020
+ sinon.stub(meeting, 'leave').resolves();
1021
+
1022
+ await assert.isRejected(
1023
+ meeting.joinWithMedia({
1024
+ joinOptions,
1025
+ mediaOptions,
1026
+ }),
1027
+ rejectError
1028
+ );
1029
+
1030
+ assert.calledOnce(meeting.join);
1031
+ assert.calledOnce(meeting.addMediaInternal);
1032
+ assert.calledOnce(Metrics.sendBehavioralMetric);
1033
+ });
1034
+
1006
1035
  it('should not call leave() if addMediaInternal() fails the first time and succeeds the second time and should only call join() once', async () => {
1007
1036
  const addMediaError = new Error('fake addMedia error');
1008
1037
  const leaveStub = sinon.stub(meeting, 'leave');
@@ -1849,7 +1878,7 @@ describe('plugin-meetings', () => {
1849
1878
  setCorrelationIdSpy = sinon.spy(meeting, 'setCorrelationId');
1850
1879
  meeting.setLocus = sinon.stub().returns(true);
1851
1880
  webex.meetings.registered = true;
1852
- meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
1881
+ sinon.stub(meeting, 'updateLLMConnection').returns(Promise.resolve());
1853
1882
  });
1854
1883
 
1855
1884
  describe('successful', () => {
@@ -2040,7 +2069,7 @@ describe('plugin-meetings', () => {
2040
2069
  const defer = new Defer();
2041
2070
 
2042
2071
  meeting.config.enableAutomaticLLM = true;
2043
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2072
+ meeting.updateLLMConnection.returns(defer.promise);
2044
2073
 
2045
2074
  const result = await meeting.join();
2046
2075
 
@@ -2051,7 +2080,7 @@ describe('plugin-meetings', () => {
2051
2080
 
2052
2081
  it('should call updateLLMConnection as part of joining if config value is set', async () => {
2053
2082
  meeting.config.enableAutomaticLLM = true;
2054
- meeting.updateLLMConnection = sinon.stub().resolves();
2083
+ meeting.updateLLMConnection.resolves();
2055
2084
 
2056
2085
  await meeting.join();
2057
2086
 
@@ -2059,7 +2088,7 @@ describe('plugin-meetings', () => {
2059
2088
  });
2060
2089
 
2061
2090
  it('should not call updateLLMConnection as part of joining if config value is not set', async () => {
2062
- meeting.updateLLMConnection = sinon.stub().resolves();
2091
+ meeting.updateLLMConnection.resolves();
2063
2092
  await meeting.join();
2064
2093
 
2065
2094
  assert.notCalled(meeting.updateLLMConnection);
@@ -2069,7 +2098,7 @@ describe('plugin-meetings', () => {
2069
2098
  const defer = new Defer();
2070
2099
 
2071
2100
  meeting.config.enableAutomaticLLM = true;
2072
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2101
+ meeting.updateLLMConnection.returns(defer.promise);
2073
2102
 
2074
2103
  const result = await meeting.join();
2075
2104
 
@@ -2095,6 +2124,42 @@ describe('plugin-meetings', () => {
2095
2124
  ]);
2096
2125
  }
2097
2126
  });
2127
+
2128
+ it('handles Locus LLM events', async () => {
2129
+ const locusInfoParseStub = sinon.stub(meeting.locusInfo, 'parse');
2130
+ sinon.stub(meeting, 'isJoined').returns(true);
2131
+
2132
+ // Set up llm.on stub to capture the registered listener when updateLLMConnection is called
2133
+ let locusLLMEventListener;
2134
+ meeting.webex.internal.llm.on = sinon.stub().callsFake((eventName, callback) => {
2135
+ if (eventName === 'event:locus.state_message') {
2136
+ locusLLMEventListener = callback;
2137
+ }
2138
+ });
2139
+ meeting.webex.internal.llm.off = sinon.stub();
2140
+
2141
+ // we need the real meeting.updateLLMConnection not the mock
2142
+ meeting.updateLLMConnection.restore();
2143
+
2144
+ // Call updateLLMConnection to register the listener
2145
+ await meeting.updateLLMConnection();
2146
+
2147
+ // Verify the listener was registered and we captured it
2148
+ assert.isDefined(locusLLMEventListener, 'LLM event listener should be registered');
2149
+
2150
+ // Now trigger the event
2151
+ const eventData = {
2152
+ eventType: 'locus.state_message',
2153
+ stateElementsMessage: {
2154
+ header: {messageId: 'msg-1'},
2155
+ elements: [],
2156
+ },
2157
+ };
2158
+
2159
+ locusLLMEventListener({data: eventData});
2160
+
2161
+ assert.calledOnceWithExactly(locusInfoParseStub, meeting, eventData);
2162
+ });
2098
2163
  });
2099
2164
 
2100
2165
  describe('refreshPermissionToken', () => {
@@ -4749,6 +4814,7 @@ describe('plugin-meetings', () => {
4749
4814
  id: 'fake locus from mocked join request',
4750
4815
  locusUrl: 'fake locus url',
4751
4816
  mediaId: 'fake media id',
4817
+ locus: {fullState: {}},
4752
4818
  });
4753
4819
  sinon.stub(meeting.meetingRequest, 'joinMeeting').resolves({
4754
4820
  headers: {
@@ -12291,16 +12357,26 @@ describe('plugin-meetings', () => {
12291
12357
  assert.notCalled(webex.internal.llm.disconnectLLM);
12292
12358
  assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a datachannel url');
12293
12359
  assert.equal(result, 'something');
12294
- assert.calledOnceWithExactly(
12360
+ assert.calledWithExactly(
12295
12361
  meeting.webex.internal.llm.off,
12296
12362
  'event:relay.event',
12297
12363
  meeting.processRelayEvent
12298
12364
  );
12299
- assert.calledOnceWithExactly(
12365
+ assert.calledWithExactly(
12366
+ meeting.webex.internal.llm.off,
12367
+ 'event:locus.state_message',
12368
+ meeting.processLocusLLMEvent
12369
+ );
12370
+ assert.calledWithExactly(
12300
12371
  meeting.webex.internal.llm.on,
12301
12372
  'event:relay.event',
12302
12373
  meeting.processRelayEvent
12303
12374
  );
12375
+ assert.calledWithExactly(
12376
+ meeting.webex.internal.llm.on,
12377
+ 'event:locus.state_message',
12378
+ meeting.processLocusLLMEvent
12379
+ );
12304
12380
  });
12305
12381
 
12306
12382
  it('disconnects if first if the locus url has changed', async () => {
@@ -12328,15 +12404,25 @@ describe('plugin-meetings', () => {
12328
12404
  'event:relay.event',
12329
12405
  meeting.processRelayEvent
12330
12406
  );
12331
- assert.calledTwice(meeting.webex.internal.llm.off);
12332
- assert.calledOnceWithExactly(
12407
+ assert.calledWithExactly(
12408
+ meeting.webex.internal.llm.off,
12409
+ 'event:locus.state_message',
12410
+ meeting.processLocusLLMEvent
12411
+ );
12412
+ assert.callCount(meeting.webex.internal.llm.off, 4);
12413
+ assert.calledWithExactly(
12333
12414
  meeting.webex.internal.llm.on,
12334
12415
  'event:relay.event',
12335
12416
  meeting.processRelayEvent
12336
12417
  );
12418
+ assert.calledWithExactly(
12419
+ meeting.webex.internal.llm.on,
12420
+ 'event:locus.state_message',
12421
+ meeting.processLocusLLMEvent
12422
+ );
12337
12423
  });
12338
12424
 
12339
- it('disconnects if first if the data channel url has changed', async () => {
12425
+ it('disconnects it first if the data channel url has changed', async () => {
12340
12426
  meeting.joinedWith = {state: 'JOINED'};
12341
12427
  webex.internal.llm.isConnected.returns(true);
12342
12428
  webex.internal.llm.getLocusUrl.returns('a url');
@@ -12361,12 +12447,21 @@ describe('plugin-meetings', () => {
12361
12447
  'event:relay.event',
12362
12448
  meeting.processRelayEvent
12363
12449
  );
12364
- assert.calledTwice(meeting.webex.internal.llm.off);
12365
- assert.calledOnceWithExactly(
12450
+ assert.calledWithExactly(
12451
+ meeting.webex.internal.llm.off,
12452
+ 'event:locus.state_message',
12453
+ meeting.processLocusLLMEvent
12454
+ );
12455
+ assert.calledWithExactly(
12366
12456
  meeting.webex.internal.llm.on,
12367
12457
  'event:relay.event',
12368
12458
  meeting.processRelayEvent
12369
12459
  );
12460
+ assert.calledWithExactly(
12461
+ meeting.webex.internal.llm.on,
12462
+ 'event:locus.state_message',
12463
+ meeting.processLocusLLMEvent
12464
+ );
12370
12465
  });
12371
12466
 
12372
12467
  it('disconnects when the state is not JOINED', async () => {
@@ -12381,11 +12476,16 @@ describe('plugin-meetings', () => {
12381
12476
  assert.calledWith(webex.internal.llm.disconnectLLM, undefined);
12382
12477
  assert.notCalled(webex.internal.llm.registerAndConnect);
12383
12478
  assert.equal(result, undefined);
12384
- assert.calledOnceWithExactly(
12479
+ assert.calledWithExactly(
12385
12480
  meeting.webex.internal.llm.off,
12386
12481
  'event:relay.event',
12387
12482
  meeting.processRelayEvent
12388
12483
  );
12484
+ assert.calledWithExactly(
12485
+ meeting.webex.internal.llm.off,
12486
+ 'event:locus.state_message',
12487
+ meeting.processLocusLLMEvent
12488
+ );
12389
12489
  });
12390
12490
 
12391
12491
  it('connect ps data channel if ps started in webinar', async () => {
@@ -12411,22 +12511,22 @@ describe('plugin-meetings', () => {
12411
12511
  });
12412
12512
 
12413
12513
  it('should read the locus object, set on the meeting and return null', () => {
12514
+ const dataSets = {someFakeStuff: 'dataSet'};
12515
+
12414
12516
  meeting.setLocus({
12415
12517
  mediaConnections: [test1],
12416
12518
  locusUrl: url1,
12417
12519
  locusId: uuid1,
12418
12520
  selfId: uuid2,
12419
12521
  mediaId: uuid3,
12420
- host: {id: uuid4},
12522
+ locus: {host: {id: uuid4}},
12523
+ dataSets,
12421
12524
  });
12422
12525
  assert.calledOnce(meeting.locusInfo.initialSetup);
12423
12526
  assert.calledWith(meeting.locusInfo.initialSetup, {
12424
- mediaConnections: [test1],
12425
- locusUrl: url1,
12426
- locusId: uuid1,
12427
- selfId: uuid2,
12428
- mediaId: uuid3,
12429
- host: {id: uuid4},
12527
+ trigger: 'join-response',
12528
+ locus: {host: {id: uuid4}},
12529
+ dataSets,
12430
12530
  });
12431
12531
  assert.equal(meeting.mediaConnections, test1);
12432
12532
  assert.equal(meeting.locusUrl, url1);
@@ -1433,5 +1433,82 @@ describe('plugin-meetings', () => {
1433
1433
  });
1434
1434
  });
1435
1435
  });
1436
+
1437
+ describe('#parseLocusJoin', () => {
1438
+ let response;
1439
+
1440
+ beforeEach(() => {
1441
+ response = {
1442
+ body: {
1443
+ locus: {
1444
+ url: 'https://locus-a.wbx2.com/locus/api/v1/loci/12345',
1445
+ self: {
1446
+ id: 'selfId123',
1447
+ },
1448
+ },
1449
+ dataSets: [{name: 'dataset1', url: 'http://dataset.com'}],
1450
+ mediaConnections: [
1451
+ {mediaId: 'mediaId456'},
1452
+ {someOtherField: 'value'},
1453
+ ],
1454
+ },
1455
+ };
1456
+ });
1457
+
1458
+ it('works as expected', () => {
1459
+ const result = MeetingUtil.parseLocusJoin(response);
1460
+
1461
+ assert.deepEqual(result, {
1462
+ locus: response.body.locus,
1463
+ dataSets: response.body.dataSets,
1464
+ mediaConnections: response.body.mediaConnections,
1465
+ locusUrl: 'https://locus-a.wbx2.com/locus/api/v1/loci/12345',
1466
+ locusId: '12345',
1467
+ selfId: 'selfId123',
1468
+ mediaId: 'mediaId456',
1469
+ });
1470
+ });
1471
+
1472
+ it('extracts mediaId from the last mediaConnection that has it', () => {
1473
+ response.body.mediaConnections = [
1474
+ {someField: 'noMediaId'},
1475
+ {mediaId: 'firstMediaId'},
1476
+ {mediaId: 'secondMediaId'},
1477
+ ];
1478
+
1479
+ const result = MeetingUtil.parseLocusJoin(response);
1480
+
1481
+ // Note: the implementation uses forEach which doesn't break,
1482
+ // so it will use the last mediaId found, not the first
1483
+ assert.equal(result.mediaId, 'secondMediaId');
1484
+ });
1485
+
1486
+ it('handles empty mediaConnections array', () => {
1487
+ response.body.mediaConnections = [];
1488
+
1489
+ const result = MeetingUtil.parseLocusJoin(response);
1490
+
1491
+ assert.deepEqual(result, {
1492
+ locus: response.body.locus,
1493
+ dataSets: response.body.dataSets,
1494
+ mediaConnections: [],
1495
+ locusUrl: 'https://locus-a.wbx2.com/locus/api/v1/loci/12345',
1496
+ locusId: '12345',
1497
+ selfId: 'selfId123',
1498
+ });
1499
+ assert.isUndefined(result.mediaId);
1500
+ });
1501
+
1502
+ it('handles mediaConnections without mediaId', () => {
1503
+ response.body.mediaConnections = [
1504
+ {someField: 'value1'},
1505
+ {anotherField: 'value2'},
1506
+ ];
1507
+
1508
+ const result = MeetingUtil.parseLocusJoin(response);
1509
+
1510
+ assert.isUndefined(result.mediaId);
1511
+ });
1512
+ });
1436
1513
  });
1437
1514
  });
@@ -914,7 +914,11 @@ describe('plugin-meetings', () => {
914
914
  'LOCUS_ID'
915
915
  );
916
916
  assert.calledWith(initialSetup, {
917
- url: url1,
917
+ trigger: 'get-loci-response',
918
+ locus: {
919
+ url: url1,
920
+ },
921
+ hashTreeMessage: undefined
918
922
  });
919
923
  });
920
924
  });
@@ -1415,21 +1419,53 @@ describe('plugin-meetings', () => {
1415
1419
  );
1416
1420
  assert.calledOnce(initialSetup);
1417
1421
  assert.calledWith(initialSetup, {
1418
- id: uuid1,
1419
- replaces: [
1420
- {
1421
- locusUrl: 'http:locusUrl',
1422
+ trigger: 'locus-message',
1423
+ locus: {
1424
+ id: uuid1,
1425
+ replaces: [
1426
+ {
1427
+ locusUrl: 'http:locusUrl',
1428
+ },
1429
+ ],
1430
+ self: {
1431
+ callBackInfo: {
1432
+ callbackAddress: uri1,
1433
+ },
1434
+ devices: [],
1422
1435
  },
1423
- ],
1424
- self: {
1425
- callBackInfo: {
1426
- callbackAddress: uri1,
1436
+ info: {
1437
+ webExMeetingId,
1427
1438
  },
1428
- devices: [],
1429
1439
  },
1440
+ hashTreeMessage: undefined
1441
+ });
1442
+ });
1443
+ it('should setup the meeting from a hash tree event', async () => {
1444
+ const locus = {
1445
+ id: uuid1,
1446
+ self: {},
1430
1447
  info: {
1431
1448
  webExMeetingId,
1432
1449
  },
1450
+ };
1451
+ const hashTreeMessage = {something: 'hashTreeData'};
1452
+ await webex.meetings.handleLocusEvent({
1453
+ locus,
1454
+ eventType: 'locus.state_message',
1455
+ locusUrl: url1,
1456
+ stateElementsMessage: hashTreeMessage,
1457
+ });
1458
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
1459
+ assert.calledWith(
1460
+ webex.meetings.meetingCollection.getByKey,
1461
+ 'meetingNumber',
1462
+ webExMeetingId
1463
+ );
1464
+ assert.calledOnce(initialSetup);
1465
+ assert.calledWith(initialSetup, {
1466
+ trigger: 'locus-message',
1467
+ locus,
1468
+ hashTreeMessage,
1433
1469
  });
1434
1470
  });
1435
1471
  it('should setup the meeting by difference event without replaces', async () => {
@@ -1458,16 +1494,20 @@ describe('plugin-meetings', () => {
1458
1494
  );
1459
1495
  assert.calledOnce(initialSetup);
1460
1496
  assert.calledWith(initialSetup, {
1461
- id: uuid1,
1462
- self: {
1463
- callBackInfo: {
1464
- callbackAddress: uri1,
1497
+ trigger: 'locus-message',
1498
+ locus: {
1499
+ id: uuid1,
1500
+ self: {
1501
+ callBackInfo: {
1502
+ callbackAddress: uri1,
1503
+ },
1504
+ devices: [],
1505
+ },
1506
+ info: {
1507
+ webExMeetingId,
1465
1508
  },
1466
- devices: [],
1467
- },
1468
- info: {
1469
- webExMeetingId,
1470
1509
  },
1510
+ hashTreeMessage: undefined
1471
1511
  });
1472
1512
  });
1473
1513
 
@@ -1530,16 +1570,20 @@ describe('plugin-meetings', () => {
1530
1570
  );
1531
1571
  assert.calledOnce(initialSetup);
1532
1572
  assert.calledWith(initialSetup, {
1533
- id: uuid1,
1534
- self: {
1535
- callBackInfo: {
1536
- callbackAddress: uri1,
1573
+ trigger: 'locus-message',
1574
+ locus: {
1575
+ id: uuid1,
1576
+ self: {
1577
+ callBackInfo: {
1578
+ callbackAddress: uri1,
1579
+ },
1580
+ devices: [],
1581
+ },
1582
+ info: {
1583
+ webExMeetingId,
1537
1584
  },
1538
- devices: [],
1539
- },
1540
- info: {
1541
- webExMeetingId,
1542
1585
  },
1586
+ hashTreeMessage: undefined
1543
1587
  });
1544
1588
  });
1545
1589
 
@@ -3087,6 +3131,7 @@ describe('plugin-meetings', () => {
3087
3131
  },
3088
3132
  });
3089
3133
  assert.calledWith(webex.meetings.handleLocusEvent, {
3134
+ eventType: LOCUSEVENT.SDK_NO_EVENT,
3090
3135
  locus: breakoutLocus,
3091
3136
  locusUrl: breakoutLocus.url,
3092
3137
  });