@webex/plugin-meetings 3.10.0-next.27 → 3.10.0-next.29

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 (46) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/config.js +2 -1
  4. package/dist/config.js.map +1 -1
  5. package/dist/hashTree/hashTreeParser.js +3 -14
  6. package/dist/hashTree/hashTreeParser.js.map +1 -1
  7. package/dist/hashTree/types.js.map +1 -1
  8. package/dist/hashTree/utils.js +11 -0
  9. package/dist/hashTree/utils.js.map +1 -1
  10. package/dist/interpretation/index.js +1 -1
  11. package/dist/interpretation/siLanguage.js +1 -1
  12. package/dist/locus-info/index.js +2 -1
  13. package/dist/locus-info/index.js.map +1 -1
  14. package/dist/meetings/index.js +25 -11
  15. package/dist/meetings/index.js.map +1 -1
  16. package/dist/meetings/util.js +11 -7
  17. package/dist/meetings/util.js.map +1 -1
  18. package/dist/reachability/clusterReachability.js +171 -18
  19. package/dist/reachability/clusterReachability.js.map +1 -1
  20. package/dist/reachability/index.js +3 -1
  21. package/dist/reachability/index.js.map +1 -1
  22. package/dist/reachability/reachabilityPeerConnection.js +1 -1
  23. package/dist/reachability/reachabilityPeerConnection.js.map +1 -1
  24. package/dist/types/config.d.ts +1 -0
  25. package/dist/types/hashTree/hashTreeParser.d.ts +1 -11
  26. package/dist/types/hashTree/types.d.ts +4 -0
  27. package/dist/types/hashTree/utils.d.ts +7 -0
  28. package/dist/types/locus-info/index.d.ts +2 -1
  29. package/dist/types/reachability/clusterReachability.d.ts +30 -3
  30. package/dist/webinar/index.js +1 -1
  31. package/package.json +1 -1
  32. package/src/config.ts +1 -0
  33. package/src/hashTree/hashTreeParser.ts +2 -16
  34. package/src/hashTree/types.ts +5 -0
  35. package/src/hashTree/utils.ts +11 -0
  36. package/src/locus-info/index.ts +2 -3
  37. package/src/meetings/index.ts +24 -17
  38. package/src/meetings/util.ts +10 -9
  39. package/src/reachability/clusterReachability.ts +153 -27
  40. package/src/reachability/index.ts +6 -1
  41. package/src/reachability/reachabilityPeerConnection.ts +3 -1
  42. package/test/unit/spec/hashTree/utils.ts +38 -1
  43. package/test/unit/spec/meetings/index.js +192 -1
  44. package/test/unit/spec/meetings/utils.js +51 -1
  45. package/test/unit/spec/reachability/clusterReachability.ts +125 -1
  46. package/test/unit/spec/reachability/index.ts +3 -3
@@ -2911,7 +2911,7 @@ describe('plugin-meetings', () => {
2911
2911
  conversationUrl: 'conversationUrl1',
2912
2912
  };
2913
2913
 
2914
- sinon.stub(MeetingsUtil, 'checkForCorrelationId').returns('correlationId1');
2914
+ sinon.stub(MeetingsUtil, 'getCorrelationIdForDevice').returns('correlationId1');
2915
2915
  });
2916
2916
  afterEach(() => {
2917
2917
  sinon.restore();
@@ -3017,6 +3017,197 @@ describe('plugin-meetings', () => {
3017
3017
  );
3018
3018
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', '123456');
3019
3019
  });
3020
+
3021
+ describe('when receiving hash tree events', () => {
3022
+ let hashTreeEvent;
3023
+
3024
+ beforeEach(() => {
3025
+ MeetingsUtil.getCorrelationIdForDevice.restore();
3026
+ sinon.spy(MeetingsUtil, 'getCorrelationIdForDevice');
3027
+
3028
+ hashTreeEvent = {
3029
+ eventType: 'locus.state_message',
3030
+ stateElementsMessage: {
3031
+ locusUrl: url1,
3032
+ locusStateElements: [
3033
+ {
3034
+ htMeta: {
3035
+ elementId: {
3036
+ type: 'participant',
3037
+ id: 2,
3038
+ version: 1,
3039
+ },
3040
+ dataSetNames: ['main'],
3041
+ },
3042
+ data: {
3043
+ id: 'participant1',
3044
+ },
3045
+ },
3046
+ {
3047
+ htMeta: {
3048
+ elementId: {
3049
+ type: 'Self',
3050
+ id: 1,
3051
+ version: 1,
3052
+ },
3053
+ dataSetNames: ['self'],
3054
+ },
3055
+ data: {
3056
+ callbackInfo: {
3057
+ callbackAddress: 'address1',
3058
+ },
3059
+ devices: [
3060
+ {
3061
+ url: 'deviceUrl',
3062
+ correlationId: 'correlationId1',
3063
+ },
3064
+ ],
3065
+ },
3066
+ },
3067
+ ],
3068
+ },
3069
+ };
3070
+
3071
+ webex.internal.device.url = 'deviceUrl';
3072
+ });
3073
+
3074
+ it('should find meeting by locusUrl from stateElementsMessage', () => {
3075
+ mockGetByKey('locusUrl');
3076
+ const result = webex.meetings.getCorrespondingMeetingByLocus(hashTreeEvent);
3077
+ assert.deepEqual(result, mockReturnMeeting);
3078
+ assert.calledOnceWithExactly(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
3079
+ });
3080
+
3081
+ it('should extract self data from locusStateElements and try correlationId when locusUrl not found', () => {
3082
+ mockGetByKey('correlationId');
3083
+ const result = webex.meetings.getCorrespondingMeetingByLocus(hashTreeEvent);
3084
+ assert.deepEqual(result, mockReturnMeeting);
3085
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 2);
3086
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
3087
+ assert.calledWith(
3088
+ webex.meetings.meetingCollection.getByKey,
3089
+ 'correlationId',
3090
+ 'correlationId1'
3091
+ );
3092
+ assert.calledOnceWithExactly(
3093
+ MeetingsUtil.getCorrelationIdForDevice,
3094
+ 'deviceUrl',
3095
+ hashTreeEvent.stateElementsMessage.locusStateElements[1].data
3096
+ );
3097
+ });
3098
+
3099
+ it('should extract self data from locusStateElements and try sipUri when locusUrl and correlationId not found', () => {
3100
+ mockGetByKey('sipUri');
3101
+ const result = webex.meetings.getCorrespondingMeetingByLocus(hashTreeEvent);
3102
+ assert.deepEqual(result, mockReturnMeeting);
3103
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 3);
3104
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
3105
+ assert.calledWith(
3106
+ webex.meetings.meetingCollection.getByKey,
3107
+ 'correlationId',
3108
+ 'correlationId1'
3109
+ );
3110
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
3111
+ });
3112
+
3113
+ it('should try all keys when no meeting found', () => {
3114
+ mockGetByKey();
3115
+ const result = webex.meetings.getCorrespondingMeetingByLocus(hashTreeEvent);
3116
+ assert.isNull(result);
3117
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
3118
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
3119
+ assert.calledWith(
3120
+ webex.meetings.meetingCollection.getByKey,
3121
+ 'correlationId',
3122
+ 'correlationId1'
3123
+ );
3124
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
3125
+ // these remaining 2 will never work for hash trees, but just checking that
3126
+ // the calls are made and we don't crash
3127
+ assert.calledWith(
3128
+ webex.meetings.meetingCollection.getByKey,
3129
+ 'conversationUrl',
3130
+ undefined
3131
+ );
3132
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', undefined);
3133
+ });
3134
+
3135
+ it('should handle hash tree event with no self object', () => {
3136
+ mockGetByKey();
3137
+ hashTreeEvent.stateElementsMessage.locusStateElements = [
3138
+ {
3139
+ htMeta: {
3140
+ elementId: {
3141
+ type: 'participant',
3142
+ id: 2,
3143
+ version: 1,
3144
+ },
3145
+ dataSetNames: ['dataset1'],
3146
+ },
3147
+ data: {
3148
+ id: 'participant1',
3149
+ },
3150
+ },
3151
+ ];
3152
+
3153
+ const result = webex.meetings.getCorrespondingMeetingByLocus(hashTreeEvent);
3154
+ assert.isNull(result);
3155
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
3156
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
3157
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'correlationId', false);
3158
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', undefined);
3159
+ // these remaining 2 will never work for hash trees, but just checking that
3160
+ // the calls are made and we don't crash
3161
+ assert.calledWith(
3162
+ webex.meetings.meetingCollection.getByKey,
3163
+ 'conversationUrl',
3164
+ undefined
3165
+ );
3166
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', undefined);
3167
+ });
3168
+
3169
+ it('should handle hash tree event with empty locusStateElements', () => {
3170
+ mockGetByKey();
3171
+ hashTreeEvent.stateElementsMessage.locusStateElements = [];
3172
+ const result = webex.meetings.getCorrespondingMeetingByLocus(hashTreeEvent);
3173
+ assert.isNull(result);
3174
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
3175
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
3176
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'correlationId', false);
3177
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', undefined);
3178
+ // these remaining 2 will never work for hash trees, but just checking that
3179
+ // the calls are made and we don't crash
3180
+ assert.calledWith(
3181
+ webex.meetings.meetingCollection.getByKey,
3182
+ 'conversationUrl',
3183
+ undefined
3184
+ );
3185
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', undefined);
3186
+ });
3187
+
3188
+ it('should handle hash tree event with self object but no callbackAddress', () => {
3189
+ mockGetByKey('meetingNumber');
3190
+ delete hashTreeEvent.stateElementsMessage.locusStateElements[1].data.callbackInfo;
3191
+ const result = webex.meetings.getCorrespondingMeetingByLocus(hashTreeEvent);
3192
+ assert.deepEqual(result, mockReturnMeeting);
3193
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
3194
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
3195
+ assert.calledWith(
3196
+ webex.meetings.meetingCollection.getByKey,
3197
+ 'correlationId',
3198
+ 'correlationId1'
3199
+ );
3200
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', undefined);
3201
+ // these remaining 2 will never work for hash trees, but just checking that
3202
+ // the calls are made and we don't crash
3203
+ assert.calledWith(
3204
+ webex.meetings.meetingCollection.getByKey,
3205
+ 'conversationUrl',
3206
+ undefined
3207
+ );
3208
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', undefined);
3209
+ });
3210
+ });
3020
3211
  });
3021
3212
 
3022
3213
  describe('#sortLocusArrayToUpdate', () => {
@@ -299,5 +299,55 @@ describe('plugin-meetings', () => {
299
299
  const sdp2 = 'v=0\r\no=HOMER 0 1 IN IP4 23.89.67.81\r\ns=-\r\nc=IN IP4 23.89.67.81\r\nb=TIAS:128000\r\nt=0 0\r\na=ice-lite\r\n'
300
300
  assert.equal(MeetingsUtil.getMediaServer(sdp2), 'homer');
301
301
  });
302
- })
302
+ });
303
+
304
+ describe('#getCorrelationIdForDevice', () => {
305
+ it('should return correlationId if device with matching url is found', () => {
306
+ const locusSelf = {
307
+ devices: [
308
+ {url: 'deviceUrl1', correlationId: 'correlationId1'},
309
+ {url: 'deviceUrl2', correlationId: 'correlationId2'},
310
+ ],
311
+ };
312
+
313
+ const correlationId = MeetingsUtil.getCorrelationIdForDevice('deviceUrl1', locusSelf);
314
+ assert.equal(correlationId, 'correlationId1');
315
+ });
316
+
317
+ it('should return false if no device with matching url is found', () => {
318
+ const locusSelf = {
319
+ devices: [
320
+ {url: 'deviceUrl1', correlationId: 'correlationId1'},
321
+ {url: 'deviceUrl2', correlationId: 'correlationId2'},
322
+ ],
323
+ };
324
+
325
+ const correlationId = MeetingsUtil.getCorrelationIdForDevice('deviceUrl3', locusSelf);
326
+ assert.equal(correlationId, false);
327
+ });
328
+
329
+ it('should return false if device with matching url has no correlationId', () => {
330
+ const locusSelf = {
331
+ devices: [{url: 'deviceUrl1'}, {url: 'deviceUrl2', correlationId: 'correlationId2'}],
332
+ };
333
+
334
+ const correlationId = MeetingsUtil.getCorrelationIdForDevice('deviceUrl1', locusSelf);
335
+ assert.equal(correlationId, false);
336
+ });
337
+
338
+ it('should return false if locusSelf has no devices', () => {
339
+ const locusSelf = {};
340
+
341
+ const correlationId = MeetingsUtil.getCorrelationIdForDevice('deviceUrl1', locusSelf);
342
+ assert.equal(correlationId, false);
343
+ });
344
+
345
+ it('should return false if locusSelf is null or undefined', () => {
346
+ let correlationId = MeetingsUtil.getCorrelationIdForDevice('deviceUrl1', null);
347
+ assert.equal(correlationId, false);
348
+
349
+ correlationId = MeetingsUtil.getCorrelationIdForDevice('deviceUrl1', undefined);
350
+ assert.equal(correlationId, false);
351
+ });
352
+ });
303
353
  });
@@ -10,6 +10,7 @@ import {
10
10
  NatTypeUpdatedEventData,
11
11
  } from '@webex/plugin-meetings/src/reachability/clusterReachability';
12
12
  import {ReachabilityPeerConnection} from '@webex/plugin-meetings/src/reachability/reachabilityPeerConnection';
13
+ import {ReachabilityPeerConnectionEvents} from '@webex/plugin-meetings/src/reachability/reachability.types';
13
14
 
14
15
  describe('ClusterReachability', () => {
15
16
  let previousRTCPeerConnection;
@@ -92,6 +93,22 @@ describe('ClusterReachability', () => {
92
93
  assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated], []);
93
94
  });
94
95
 
96
+ it('should create separate peer connections when enablePerUdpUrlReachability is true', () => {
97
+ const perUdpClusterReachability = new ClusterReachability(
98
+ 'testName',
99
+ {
100
+ isVideoMesh: false,
101
+ udp: ['stun:udp1', 'stun:udp2'],
102
+ tcp: ['stun:tcp1.webex.com'],
103
+ xtls: ['stun:xtls1.webex.com'],
104
+ },
105
+ true
106
+ );
107
+
108
+ assert.equal((perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp.length, 2);
109
+ assert.instanceOf((perUdpClusterReachability as any).reachabilityPeerConnection, ReachabilityPeerConnection);
110
+ });
111
+
95
112
  describe('#event relaying', () => {
96
113
  let clock;
97
114
 
@@ -172,6 +189,44 @@ describe('ClusterReachability', () => {
172
189
  clusterReachability.abort();
173
190
  await promise;
174
191
  });
192
+
193
+ it('emits only the first successful UDP result when enablePerUdpUrlReachability is true', async () => {
194
+ const perUdpClusterReachability = new ClusterReachability(
195
+ 'testName',
196
+ {
197
+ isVideoMesh: false,
198
+ udp: ['stun:udp1', 'stun:udp2'],
199
+ tcp: [],
200
+ xtls: [],
201
+ },
202
+ true
203
+ );
204
+
205
+ const udpEvents: ResultEventData[] = [];
206
+ perUdpClusterReachability.on(Events.resultReady, (data: ResultEventData) => {
207
+ udpEvents.push(data);
208
+ });
209
+
210
+ const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
211
+ const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
212
+
213
+ udpRpc1.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.resultReady, {
214
+ protocol: 'udp',
215
+ result: 'reachable',
216
+ latencyInMilliseconds: 50,
217
+ clientMediaIPs: ['1.1.1.1'],
218
+ });
219
+
220
+ udpRpc2.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.resultReady, {
221
+ protocol: 'udp',
222
+ result: 'reachable',
223
+ latencyInMilliseconds: 30,
224
+ clientMediaIPs: ['2.2.2.2'],
225
+ });
226
+
227
+ assert.equal(udpEvents.length, 1);
228
+ assert.equal(udpEvents[0].latencyInMilliseconds, 50);
229
+ });
175
230
  });
176
231
 
177
232
  describe('#subnet collection', () => {
@@ -236,6 +291,38 @@ describe('ClusterReachability', () => {
236
291
  assert.equal(clusterReachability.reachedSubnets.size, 3);
237
292
  assert.deepEqual(Array.from(clusterReachability.reachedSubnets), ['192.168.1.1', '10.0.0.1', '172.16.0.1']);
238
293
  });
294
+
295
+ it('collects reached subnets from all peer connections when enablePerUdpUrlReachability is true', async () => {
296
+ const perUdpClusterReachability = new ClusterReachability(
297
+ 'testName',
298
+ {
299
+ isVideoMesh: false,
300
+ udp: ['stun:udp1', 'stun:udp2'],
301
+ tcp: ['stun:tcp1.webex.com'],
302
+ xtls: [],
303
+ },
304
+ true
305
+ );
306
+
307
+ const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
308
+ const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
309
+ const tcpTlsRpc = (perUdpClusterReachability as any).reachabilityPeerConnection;
310
+
311
+ udpRpc1.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
312
+ subnets: ['192.168.1.1'],
313
+ });
314
+ udpRpc2.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
315
+ subnets: ['10.0.0.1'],
316
+ });
317
+ tcpTlsRpc.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
318
+ subnets: ['172.16.0.1'],
319
+ });
320
+
321
+ assert.equal(perUdpClusterReachability.reachedSubnets.size, 3);
322
+ assert.isTrue(perUdpClusterReachability.reachedSubnets.has('192.168.1.1'));
323
+ assert.isTrue(perUdpClusterReachability.reachedSubnets.has('10.0.0.1'));
324
+ assert.isTrue(perUdpClusterReachability.reachedSubnets.has('172.16.0.1'));
325
+ });
239
326
  });
240
327
 
241
328
  describe('#delegation', () => {
@@ -277,6 +364,43 @@ describe('ClusterReachability', () => {
277
364
  assert.calledOnce(rpcGetResultStub);
278
365
  assert.deepEqual(result, expectedResult);
279
366
  });
367
+
368
+ it('delegates start() and abort() to all peer connections when enablePerUdpUrlReachability is true', async () => {
369
+ const perUdpClusterReachability = new ClusterReachability(
370
+ 'testName',
371
+ {
372
+ isVideoMesh: false,
373
+ udp: ['stun:udp1', 'stun:udp2'],
374
+ tcp: ['stun:tcp1.webex.com'],
375
+ xtls: [],
376
+ },
377
+ true
378
+ );
379
+
380
+ const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
381
+ const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
382
+ const tcpTlsRpc = (perUdpClusterReachability as any).reachabilityPeerConnection;
383
+
384
+ const startStub1 = sinon.stub(udpRpc1, 'start').resolves({udp: {result: 'reachable'}});
385
+ const startStub2 = sinon.stub(udpRpc2, 'start').resolves({udp: {result: 'unreachable'}});
386
+ const startStubTcp = sinon.stub(tcpTlsRpc, 'start').resolves({tcp: {result: 'reachable'}});
387
+
388
+ const abortStub1 = sinon.stub(udpRpc1, 'abort');
389
+ const abortStub2 = sinon.stub(udpRpc2, 'abort');
390
+ const abortStubTcp = sinon.stub(tcpTlsRpc, 'abort');
391
+
392
+ await perUdpClusterReachability.start();
393
+
394
+ assert.calledOnce(startStub1);
395
+ assert.calledOnce(startStub2);
396
+ assert.calledOnce(startStubTcp);
397
+
398
+ perUdpClusterReachability.abort();
399
+
400
+ assert.calledOnce(abortStub1);
401
+ assert.calledOnce(abortStub2);
402
+ assert.calledOnce(abortStubTcp);
403
+ });
280
404
  });
281
405
 
282
406
  describe('#WebRTC peer connection setup', () => {
@@ -616,4 +740,4 @@ describe('ClusterReachability', () => {
616
740
  });
617
741
  });
618
742
  });
619
- });
743
+ });
@@ -1693,7 +1693,7 @@ describe('gatherReachability', () => {
1693
1693
  udp: ['testUDP1', 'testUDP2'],
1694
1694
  tcp: [], // empty list because TCP is disabled in config
1695
1695
  xtls: ['testXTLS1', 'testXTLS2'],
1696
- });
1696
+ }, undefined);
1697
1697
  });
1698
1698
 
1699
1699
  it('does not do TLS reachability if it is disabled in config', async () => {
@@ -1728,7 +1728,7 @@ describe('gatherReachability', () => {
1728
1728
  udp: ['testUDP1', 'testUDP2'],
1729
1729
  tcp: ['testTCP1', 'testTCP2'],
1730
1730
  xtls: [], // empty list because TLS is disabled in config
1731
- });
1731
+ }, undefined);
1732
1732
  });
1733
1733
 
1734
1734
  it('does not do TCP or TLS reachability if it is disabled in config', async () => {
@@ -1763,7 +1763,7 @@ describe('gatherReachability', () => {
1763
1763
  udp: ['testUDP1', 'testUDP2'],
1764
1764
  tcp: [], // empty list because TCP is disabled in config
1765
1765
  xtls: [], // empty list because TLS is disabled in config
1766
- });
1766
+ }, undefined);
1767
1767
  });
1768
1768
 
1769
1769
  it('retry of getClusters is succesfull', async () => {