@webex/plugin-meetings 2.19.1 → 2.19.3

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 (60) hide show
  1. package/README.md +0 -300
  2. package/dist/constants.js +3 -206
  3. package/dist/constants.js.map +1 -1
  4. package/dist/meeting/index.js +352 -489
  5. package/dist/meeting/index.js.map +1 -1
  6. package/dist/meeting/util.js +4 -213
  7. package/dist/meeting/util.js.map +1 -1
  8. package/dist/meetings/index.js +0 -28
  9. package/dist/meetings/index.js.map +1 -1
  10. package/dist/statsAnalyzer/index.js +145 -86
  11. package/dist/statsAnalyzer/index.js.map +1 -1
  12. package/package.json +5 -7
  13. package/src/constants.ts +1 -214
  14. package/src/meeting/index.js +110 -208
  15. package/src/meeting/util.js +4 -252
  16. package/src/meetings/index.js +0 -22
  17. package/src/statsAnalyzer/index.js +164 -99
  18. package/test/integration/spec/journey.js +2 -67
  19. package/test/unit/spec/meeting/index.js +88 -29
  20. package/test/unit/spec/meeting/utils.js +0 -2
  21. package/test/unit/spec/stats-analyzer/index.js +209 -1
  22. package/dist/analyzer/analyzer.js +0 -113
  23. package/dist/analyzer/analyzer.js.map +0 -1
  24. package/dist/analyzer/calculator.js +0 -87
  25. package/dist/analyzer/calculator.js.map +0 -1
  26. package/dist/metrics/mqa-processor.js +0 -170
  27. package/dist/metrics/mqa-processor.js.map +0 -1
  28. package/dist/stats/data.js +0 -93
  29. package/dist/stats/data.js.map +0 -1
  30. package/dist/stats/events.js +0 -222
  31. package/dist/stats/events.js.map +0 -1
  32. package/dist/stats/filter.js +0 -84
  33. package/dist/stats/filter.js.map +0 -1
  34. package/dist/stats/history.js +0 -147
  35. package/dist/stats/history.js.map +0 -1
  36. package/dist/stats/index.js +0 -425
  37. package/dist/stats/index.js.map +0 -1
  38. package/dist/stats/metrics.js +0 -112
  39. package/dist/stats/metrics.js.map +0 -1
  40. package/dist/stats/stats.js +0 -592
  41. package/dist/stats/stats.js.map +0 -1
  42. package/dist/stats/stream.js +0 -156
  43. package/dist/stats/stream.js.map +0 -1
  44. package/dist/stats/transformer.js +0 -126
  45. package/dist/stats/transformer.js.map +0 -1
  46. package/dist/stats/util.js +0 -64
  47. package/dist/stats/util.js.map +0 -1
  48. package/src/analyzer/analyzer.js +0 -78
  49. package/src/analyzer/calculator.js +0 -77
  50. package/src/metrics/mqa-processor.js +0 -118
  51. package/src/stats/data.js +0 -56
  52. package/src/stats/events.js +0 -185
  53. package/src/stats/filter.js +0 -40
  54. package/src/stats/history.js +0 -107
  55. package/src/stats/index.js +0 -320
  56. package/src/stats/metrics.js +0 -95
  57. package/src/stats/stats.js +0 -477
  58. package/src/stats/stream.js +0 -108
  59. package/src/stats/transformer.js +0 -109
  60. package/src/stats/util.js +0 -44
@@ -11,6 +11,8 @@ import {Credentials} from '@webex/webex-core';
11
11
  import Support from '@webex/internal-plugin-support';
12
12
  import MockWebex from '@webex/test-helper-mock-webex';
13
13
 
14
+ import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
15
+ import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
14
16
  import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
15
17
  import Meeting from '@webex/plugin-meetings/src/meeting';
16
18
  import Members from '@webex/plugin-meetings/src/members';
@@ -18,7 +20,6 @@ import Roap from '@webex/plugin-meetings/src/roap';
18
20
  import MeetingRequest from '@webex/plugin-meetings/src/meeting/request';
19
21
  import LocusInfo from '@webex/plugin-meetings/src/locus-info';
20
22
  import MediaProperties from '@webex/plugin-meetings/src/media/properties';
21
- import WebRTCStats from '@webex/plugin-meetings/src/stats';
22
23
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
23
24
  import Media from '@webex/plugin-meetings/src/media/index';
24
25
  import PeerConnectionManager from '@webex/plugin-meetings/src/peer-connection-manager';
@@ -206,7 +207,6 @@ describe('plugin-meetings', () => {
206
207
  );
207
208
 
208
209
  meeting.members.selfId = uuid1;
209
- meeting.startMediaQualityMetrics = sinon.stub();
210
210
  });
211
211
 
212
212
  describe('meeting index', () => {
@@ -229,7 +229,6 @@ describe('plugin-meetings', () => {
229
229
  assert.isNull(meeting.audio);
230
230
  assert.isNull(meeting.video);
231
231
  assert.instanceOf(meeting.meetingFiniteStateMachine, StateMachine);
232
- assert.isNull(meeting.stats);
233
232
  assert.isNull(meeting.conversationUrl);
234
233
  assert.equal(meeting.locusUrl, url1);
235
234
  assert.isNull(meeting.sipUri);
@@ -307,19 +306,6 @@ describe('plugin-meetings', () => {
307
306
  assert.instanceOf(members, Members);
308
307
  });
309
308
  });
310
- describe('#getStats', () => {
311
- it('should have #getStats', () => {
312
- assert.exists(meeting.getStats);
313
- });
314
- it('should create stats if not exists and return WebRTCStats', () => {
315
- assert.notOk(meeting.stats);
316
- meeting.createStats = sinon.stub().returns(new WebRTCStats({}, {parent: webex}));
317
- const stats = meeting.getStats();
318
-
319
- assert.calledOnce(meeting.createStats);
320
- assert.instanceOf(stats, WebRTCStats);
321
- });
322
- });
323
309
  describe('#isAudioMuted', () => {
324
310
  it('should have #isAudioMuted', () => {
325
311
  assert.exists(meeting.invite);
@@ -901,14 +887,12 @@ describe('plugin-meetings', () => {
901
887
  Media.attachMedia = sinon.stub().returns(Promise.resolve([test1, test2]));
902
888
  meeting.setMercuryListener = sinon.stub().returns(true);
903
889
  meeting.setRemoteStream = sinon.stub().returns(true);
904
- meeting.startMediaQualityMetrics = sinon.stub();
905
890
  meeting.setMercuryListener = sinon.stub();
906
891
  meeting.roap.sendRoapMediaRequest = sinon.stub().returns(new Promise((resolve) => {
907
892
  meeting.mediaProperties.peerConnection.connectionState = CONSTANTS.CONNECTION_STATE.CONNECTED;
908
893
  resolve();
909
894
  }));
910
895
  PeerConnectionManager.setContentSlides = sinon.stub().returns(true);
911
- MeetingUtil.startInternalStats = sinon.stub();
912
896
  });
913
897
 
914
898
  it('should have #addMedia', () => {
@@ -1037,7 +1021,6 @@ describe('plugin-meetings', () => {
1037
1021
  /* statsAnalyzer is initiated inside of addMedia so there isn't
1038
1022
  * a good way to mock it without mocking the constructor
1039
1023
  */
1040
- // assert.calledOnce(meeting.startMediaQualityMetrics);
1041
1024
  });
1042
1025
 
1043
1026
  it('should attach the media and return promise', async () => {
@@ -1052,6 +1035,90 @@ describe('plugin-meetings', () => {
1052
1035
  assert.instanceOf(err, WebExMeetingsErrors);
1053
1036
  });
1054
1037
  });
1038
+
1039
+ describe('handles StatsAnalyzer events', () => {
1040
+ let prevConfigValue;
1041
+ let statsAnalyzerStub;
1042
+
1043
+ beforeEach(async () => {
1044
+ meeting.meetingState = 'ACTIVE';
1045
+ prevConfigValue = meeting.config.stats.enableStatsAnalyzer;
1046
+
1047
+ meeting.config.stats.enableStatsAnalyzer = true;
1048
+
1049
+ statsAnalyzerStub = new EventsScope();
1050
+ // mock the StatsAnalyzer constructor
1051
+ sinon.stub(StatsAnalyzerModule, 'StatsAnalyzer').returns(statsAnalyzerStub);
1052
+
1053
+ await meeting.addMedia({
1054
+ mediaSettings: {}
1055
+ });
1056
+ });
1057
+
1058
+ afterEach(() => {
1059
+ meeting.config.stats.enableStatsAnalyzer = prevConfigValue;
1060
+ });
1061
+
1062
+ it('LOCAL_MEDIA_STARTED triggers "meeting:media:local:start" event and sends metrics', async () => {
1063
+ statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.LOCAL_MEDIA_STARTED, {type: 'audio'});
1064
+
1065
+ assert.calledWith(
1066
+ TriggerProxy.trigger,
1067
+ sinon.match.instanceOf(Meeting),
1068
+ {
1069
+ file: 'meeting/index',
1070
+ function: 'addMedia'
1071
+ },
1072
+ EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
1073
+ {
1074
+ type: 'audio'
1075
+ }
1076
+ );
1077
+ assert.calledWithMatch(Metrics.postEvent, {event: eventType.SENDING_MEDIA_START, data: {mediaType: 'audio'}});
1078
+ });
1079
+
1080
+ it('LOCAL_MEDIA_STOPPED triggers the right metrics', async () => {
1081
+ statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.LOCAL_MEDIA_STOPPED, {type: 'video'});
1082
+
1083
+ assert.calledWithMatch(Metrics.postEvent, {event: eventType.SENDING_MEDIA_STOP, data: {mediaType: 'video'}});
1084
+ });
1085
+
1086
+ it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics', async () => {
1087
+ statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STARTED, {type: 'video'});
1088
+
1089
+ assert.calledWith(
1090
+ TriggerProxy.trigger,
1091
+ sinon.match.instanceOf(Meeting),
1092
+ {
1093
+ file: 'meeting/index',
1094
+ function: 'addMedia'
1095
+ },
1096
+ EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
1097
+ {
1098
+ type: 'video'
1099
+ }
1100
+ );
1101
+ assert.calledWithMatch(Metrics.postEvent, {event: eventType.RECEIVING_MEDIA_START, data: {mediaType: 'video'}});
1102
+ });
1103
+
1104
+ it('REMOTE_MEDIA_STOPPED triggers the right metrics', async () => {
1105
+ statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STOPPED, {type: 'audio'});
1106
+
1107
+ assert.calledWithMatch(Metrics.postEvent, {event: eventType.RECEIVING_MEDIA_STOP, data: {mediaType: 'audio'}});
1108
+ });
1109
+
1110
+ it('MEDIA_QUALITY triggers the right metrics', async () => {
1111
+ const fakeData = {intervalMetadata: {bla: 'bla'}};
1112
+
1113
+ statsAnalyzerStub.emit(
1114
+ {file: 'test', function: 'test'},
1115
+ StatsAnalyzerModule.EVENTS.MEDIA_QUALITY,
1116
+ {data: fakeData, networkType: 'wifi'}
1117
+ );
1118
+
1119
+ assert.calledWithMatch(Metrics.postEvent, {event: eventType.MEDIA_QUALITY, data: {intervalData: fakeData, networkType: 'wifi'}});
1120
+ });
1121
+ });
1055
1122
  });
1056
1123
  describe('#acknowledge', () => {
1057
1124
  it('should have #acknowledge', () => {
@@ -1123,7 +1190,7 @@ describe('plugin-meetings', () => {
1123
1190
  meeting.unsetLocalVideoTrack = sinon.stub().returns(true);
1124
1191
  meeting.unsetLocalShareTrack = sinon.stub().returns(true);
1125
1192
  meeting.unsetRemoteTracks = sinon.stub();
1126
- meeting.statsAnalyzer = {stopAnalyzer: sinon.stub()};
1193
+ meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
1127
1194
  meeting.unsetRemoteStream = sinon.stub().returns(true);
1128
1195
  meeting.unsetPeerConnections = sinon.stub().returns(true);
1129
1196
  meeting.roap.stop = sinon.stub().returns(Promise.resolve());
@@ -2618,7 +2685,7 @@ describe('plugin-meetings', () => {
2618
2685
  meeting.unsetLocalVideoTrack = sinon.stub().returns(true);
2619
2686
  meeting.unsetLocalShareTrack = sinon.stub().returns(true);
2620
2687
  meeting.unsetRemoteTracks = sinon.stub();
2621
- meeting.statsAnalyzer = {stopAnalyzer: sinon.stub()};
2688
+ meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
2622
2689
  meeting.unsetRemoteStream = sinon.stub().returns(true);
2623
2690
  meeting.unsetPeerConnections = sinon.stub().returns(true);
2624
2691
  meeting.roap.stop = sinon.stub().returns(Promise.resolve());
@@ -3306,14 +3373,6 @@ describe('plugin-meetings', () => {
3306
3373
  assert.calledOnce(meeting.mediaProperties.unsetPeerConnection);
3307
3374
  });
3308
3375
  });
3309
- describe('#createStats', () => {
3310
- it('should create stats for the meeting object', () => {
3311
- const stats = meeting.createStats();
3312
-
3313
- assert.instanceOf(stats, WebRTCStats);
3314
- assert.instanceOf(meeting.getStats(), WebRTCStats);
3315
- });
3316
- });
3317
3376
  describe('#parseMeetingInfo', () => {
3318
3377
  const checkParseMeetingInfo = (expectedInfoToParse) => {
3319
3378
  assert.equal(meeting.conversationUrl, expectedInfoToParse.conversationUrl);
@@ -40,7 +40,6 @@ describe('plugin-meetings', () => {
40
40
  meeting.unsetLocalShareTrack = sinon.stub();
41
41
  meeting.unsetRemoteTracks = sinon.stub();
42
42
  meeting.unsetPeerConnections = sinon.stub();
43
- meeting.cleanMQAInterval = sinon.stub();
44
43
  meeting.reconnectionManager = {cleanUp: sinon.stub()};
45
44
  meeting.roap = {stop: sinon.stub()};
46
45
  });
@@ -62,7 +61,6 @@ describe('plugin-meetings', () => {
62
61
  assert.calledOnce(meeting.unsetLocalShareTrack);
63
62
  assert.calledOnce(meeting.unsetRemoteTracks);
64
63
  assert.calledOnce(meeting.unsetPeerConnections);
65
- assert.calledOnce(meeting.cleanMQAInterval);
66
64
  assert.calledOnce(meeting.reconnectionManager.cleanUp);
67
65
  assert.calledOnce(meeting.roap.stop);
68
66
  });
@@ -3,8 +3,9 @@ import chai from 'chai';
3
3
  import chaiAsPromised from 'chai-as-promised';
4
4
  import sinon from 'sinon';
5
5
 
6
- import StatsAnalyzer from '../../../../src/statsAnalyzer';
6
+ import {StatsAnalyzer, EVENTS} from '../../../../src/statsAnalyzer';
7
7
  import NetworkQualityMonitor from '../../../../src/networkQualityMonitor';
8
+ import testUtils from '../../../utils/testUtils';
8
9
 
9
10
  const {assert} = chai;
10
11
 
@@ -70,5 +71,212 @@ describe('plugin-meetings', () => {
70
71
  }));
71
72
  });
72
73
  });
74
+
75
+ describe('startAnalyzer', () => {
76
+ let clock;
77
+ let pc;
78
+ let networkQualityMonitor;
79
+ let statsAnalyzer;
80
+
81
+ let receivedEventsData = {
82
+ local: {},
83
+ remote: {},
84
+ };
85
+
86
+ const initialConfig = {
87
+ analyzerInterval: 1000,
88
+ };
89
+
90
+ let fakeStats;
91
+
92
+ const resetReceivedEvents = () => {
93
+ receivedEventsData = {
94
+ local: {},
95
+ remote: {},
96
+ };
97
+ };
98
+
99
+ beforeEach(() => {
100
+ clock = sinon.useFakeTimers();
101
+
102
+ resetReceivedEvents();
103
+
104
+ // bytesReceived and bytesSent need to be non-zero in order for StatsAnalyzer to parse any other values
105
+ fakeStats = {
106
+ audio: {
107
+ receiver: {
108
+ type: 'inbound-rtp',
109
+ packetsReceived: 0,
110
+ bytesReceived: 1,
111
+ },
112
+ sender: {
113
+ type: 'outbound-rtp',
114
+ packetsSent: 0,
115
+ bytesSent: 1,
116
+ }
117
+ },
118
+ video: {
119
+ receiver: {
120
+ type: 'inbound-rtp',
121
+ framesDecoded: 0,
122
+ bytesReceived: 1,
123
+ },
124
+ sender: {
125
+ type: 'outbound-rtp',
126
+ framesSent: 0,
127
+ bytesSent: 1,
128
+ }
129
+ }
130
+ };
131
+
132
+ pc = {
133
+ audioTransceiver: {
134
+ sender: {
135
+ getStats: sinon.stub().resolves([fakeStats.audio.sender])
136
+ },
137
+ receiver: {
138
+ getStats: sinon.stub().resolves([fakeStats.audio.receiver])
139
+ },
140
+ },
141
+ videoTransceiver: {
142
+ sender: {
143
+ getStats: sinon.stub().resolves([fakeStats.video.sender])
144
+ },
145
+ receiver: {
146
+ getStats: sinon.stub().resolves([fakeStats.video.receiver])
147
+ },
148
+ },
149
+ shareTransceiver: {
150
+ sender: {
151
+ getStats: sinon.stub().resolves([])
152
+ },
153
+ receiver: {
154
+ getStats: sinon.stub().resolves([])
155
+ },
156
+ }
157
+ };
158
+
159
+ networkQualityMonitor = new NetworkQualityMonitor(initialConfig);
160
+
161
+ statsAnalyzer = new StatsAnalyzer(initialConfig, networkQualityMonitor);
162
+
163
+ statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STARTED, (data) => {
164
+ receivedEventsData.local.started = data;
165
+ });
166
+ statsAnalyzer.on(EVENTS.LOCAL_MEDIA_STOPPED, (data) => {
167
+ receivedEventsData.local.stopped = data;
168
+ });
169
+ statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STARTED, (data) => {
170
+ receivedEventsData.remote.started = data;
171
+ });
172
+ statsAnalyzer.on(EVENTS.REMOTE_MEDIA_STOPPED, (data) => {
173
+ receivedEventsData.remote.stopped = data;
174
+ });
175
+ });
176
+
177
+ afterEach(() => {
178
+ clock.restore();
179
+ });
180
+
181
+ const startStatsAnalyzer = async (mediaStatus) => {
182
+ statsAnalyzer.updateMediaStatus(mediaStatus);
183
+ statsAnalyzer.startAnalyzer(pc);
184
+
185
+ await testUtils.flushPromises();
186
+ };
187
+
188
+ const progressTime = async () => {
189
+ await clock.tickAsync(initialConfig.analyzerInterval);
190
+ await testUtils.flushPromises();
191
+ };
192
+
193
+ const checkReceivedEvent = ({expected}) => {
194
+ // check that we got the REMOTE_MEDIA_STARTED event for audio
195
+ assert.deepEqual(receivedEventsData.local.started, expected.local?.started);
196
+ assert.deepEqual(receivedEventsData.local.stopped, expected.local?.stopped);
197
+ assert.deepEqual(receivedEventsData.remote.started, expected.remote?.started);
198
+ assert.deepEqual(receivedEventsData.remote.stopped, expected.remote?.stopped);
199
+ };
200
+
201
+ it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for audio', async () => {
202
+ await startStatsAnalyzer({expected: {sendAudio: true}});
203
+
204
+ // check that we haven't received any events yet
205
+ checkReceivedEvent({expected: {}});
206
+
207
+ // setup a mock to return some values higher the previous ones
208
+ fakeStats.audio.sender.packetsSent += 10;
209
+
210
+ await progressTime();
211
+
212
+ // check that we got the LOCAL_MEDIA_STARTED event for audio
213
+ checkReceivedEvent({expected: {local: {started: {type: 'audio'}}}});
214
+
215
+ // now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
216
+ resetReceivedEvents();
217
+ await progressTime();
218
+ checkReceivedEvent({expected: {local: {stopped: {type: 'audio'}}}});
219
+ });
220
+
221
+ it('emits LOCAL_MEDIA_STARTED and LOCAL_MEDIA_STOPPED events for video', async () => {
222
+ await startStatsAnalyzer({expected: {sendVideo: true}});
223
+
224
+ // check that we haven't received any events yet
225
+ checkReceivedEvent({expected: {}});
226
+
227
+ // setup a mock to return some values higher the previous ones
228
+ fakeStats.video.sender.framesSent += 1;
229
+
230
+ await progressTime();
231
+
232
+ // check that we got the LOCAL_MEDIA_STARTED event for audio
233
+ checkReceivedEvent({expected: {local: {started: {type: 'video'}}}});
234
+
235
+ // now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
236
+ resetReceivedEvents();
237
+ await progressTime();
238
+ checkReceivedEvent({expected: {local: {stopped: {type: 'video'}}}});
239
+ });
240
+
241
+ it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for audio', async () => {
242
+ await startStatsAnalyzer({expected: {receiveAudio: true}});
243
+
244
+ // check that we haven't received any events yet
245
+ checkReceivedEvent({expected: {}});
246
+
247
+ // setup a mock to return some values higher the previous ones
248
+ fakeStats.audio.receiver.packetsReceived += 5;
249
+
250
+ await progressTime();
251
+ // check that we got the REMOTE_MEDIA_STARTED event for audio
252
+ checkReceivedEvent({expected: {remote: {started: {type: 'audio'}}}});
253
+
254
+ // now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
255
+ resetReceivedEvents();
256
+ await progressTime();
257
+
258
+ checkReceivedEvent({expected: {remote: {stopped: {type: 'audio'}}}});
259
+ });
260
+
261
+ it('emits REMOTE_MEDIA_STARTED and REMOTE_MEDIA_STOPPED events for video', async () => {
262
+ await startStatsAnalyzer({expected: {receiveVideo: true}});
263
+
264
+ // check that we haven't received any events yet
265
+ checkReceivedEvent({expected: {}});
266
+
267
+ // setup a mock to return some values higher the previous ones
268
+ fakeStats.video.receiver.framesDecoded += 1;
269
+
270
+ await progressTime();
271
+ // check that we got the REMOTE_MEDIA_STARTED event for video
272
+ checkReceivedEvent({expected: {remote: {started: {type: 'video'}}}});
273
+
274
+ // now advance the clock and the mock still returns same values, so only "stopped" event should be triggered
275
+ resetReceivedEvents();
276
+ await progressTime();
277
+
278
+ checkReceivedEvent({expected: {remote: {stopped: {type: 'video'}}}});
279
+ });
280
+ });
73
281
  });
74
282
  });
@@ -1,113 +0,0 @@
1
- "use strict";
2
-
3
- var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
4
-
5
- var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
6
-
7
- _Object$defineProperty(exports, "__esModule", {
8
- value: true
9
- });
10
-
11
- exports.default = void 0;
12
-
13
- var _set = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/set"));
14
-
15
- var _isArray2 = _interopRequireDefault(require("lodash/isArray"));
16
-
17
- var _isFinite2 = _interopRequireDefault(require("lodash/isFinite"));
18
-
19
- var _forEach2 = _interopRequireDefault(require("lodash/forEach"));
20
-
21
- var _constants = require("../constants");
22
-
23
- var _parameter = _interopRequireDefault(require("../common/errors/parameter"));
24
-
25
- var StatsAnalyzer = {};
26
- /**
27
- * Can involve changing of the default plugin-meetings sdk for deeper results
28
- * @param {Array} series of WebRTCData
29
- * @param {Object} options
30
- * @param {Array} options.analysisKeys [{key: 'bytesSent', check: 'increasing'}, {key: 'bytesReceived', check: 'increasing'}]
31
- * @returns {Object} analysis {valid: true/false, failed: { key: [number] }, data: { webRtcKeyToAnalyze: { valid: true/false, reports: [ { value: number, valid: true/false, difference: number } ] } } }
32
- * @public
33
- */
34
-
35
- StatsAnalyzer.analyze = function (series) {
36
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
37
- analysisKeys: _constants.ANALYSIS_STATS.DEFAULT_KEYS
38
- };
39
-
40
- if (!(0, _isArray2.default)(series) || !series.length || !options || !(0, _isArray2.default)(options.analysisKeys) || !options.analysisKeys.length) {
41
- throw new _parameter.default('analyzer->analyze#series must be defined as a nonempty array of WebRTCData objects, and options.analysisKeys must be a nonempty array of strings, representing the properties to analyze.');
42
- }
43
-
44
- var properties = new _set.default(options.analysisKeys);
45
- var analysis = {
46
- valid: true,
47
- failed: {},
48
- data: {}
49
- };
50
- properties.forEach(function (config) {
51
- var property = config.key;
52
- analysis.data[property] = {
53
- valid: true,
54
- reports: []
55
- };
56
- analysis.failed[property] = [];
57
- var previous = {
58
- value: 0
59
- };
60
- var index = 0;
61
-
62
- var _loop = function _loop(i) {
63
- var singular = {};
64
- (0, _forEach2.default)(series[i].data.getData()[config.prop], function (webrtcData) {
65
- // eslint-disable-line
66
- var value = webrtcData[property];
67
-
68
- if (!value || !(0, _isFinite2.default)(value)) {
69
- return;
70
- }
71
-
72
- singular.value = value;
73
- singular.difference = 0;
74
- singular.valid = false;
75
- singular.index = index;
76
- singular.difference = singular.value - previous.value;
77
-
78
- if (config.check === _constants.ANALYSIS_CHECKS.INCREASING && singular.difference > 0) {
79
- singular.valid = true;
80
- } else if (config.check === _constants.ANALYSIS_CHECKS.DECREASING && singular.difference < 0) {
81
- singular.valid = true;
82
- } else if (config.check === _constants.ANALYSIS_CHECKS.CONSTANT) {
83
- singular.valid = true;
84
- } else {
85
- singular.valid = false;
86
- }
87
-
88
- if (!singular.valid) {
89
- analysis.data[property].valid = false;
90
- analysis.valid = false;
91
- analysis.failed[property].push(i);
92
- }
93
-
94
- previous = singular;
95
- analysis.data[property].reports.push(singular);
96
- });
97
- index += 1;
98
- };
99
-
100
- for (var i = series.length - 1; i > 0; i -= 1) {
101
- _loop(i);
102
- }
103
-
104
- if (!analysis.data[property].valid) {
105
- analysis.valid = false;
106
- }
107
- });
108
- return analysis;
109
- };
110
-
111
- var _default = StatsAnalyzer;
112
- exports.default = _default;
113
- //# sourceMappingURL=analyzer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["StatsAnalyzer","analyze","series","options","analysisKeys","ANALYSIS_STATS","DEFAULT_KEYS","length","ParameterError","properties","analysis","valid","failed","data","forEach","config","property","key","reports","previous","value","index","i","singular","getData","prop","webrtcData","difference","check","ANALYSIS_CHECKS","INCREASING","DECREASING","CONSTANT","push"],"sources":["analyzer.js"],"sourcesContent":["import {forEach, isFinite, isArray} from 'lodash';\n\nimport {\n ANALYSIS_STATS,\n ANALYSIS_CHECKS\n} from '../constants';\nimport ParameterError from '../common/errors/parameter';\n\nconst StatsAnalyzer = {};\n\n/**\n * Can involve changing of the default plugin-meetings sdk for deeper results\n * @param {Array} series of WebRTCData\n * @param {Object} options\n * @param {Array} options.analysisKeys [{key: 'bytesSent', check: 'increasing'}, {key: 'bytesReceived', check: 'increasing'}]\n * @returns {Object} analysis {valid: true/false, failed: { key: [number] }, data: { webRtcKeyToAnalyze: { valid: true/false, reports: [ { value: number, valid: true/false, difference: number } ] } } }\n * @public\n */\nStatsAnalyzer.analyze = (series, options = {analysisKeys: ANALYSIS_STATS.DEFAULT_KEYS}) => {\n if (!isArray(series) || !series.length || !options || !isArray(options.analysisKeys) || !options.analysisKeys.length) {\n throw new ParameterError('analyzer->analyze#series must be defined as a nonempty array of WebRTCData objects, and options.analysisKeys must be a nonempty array of strings, representing the properties to analyze.');\n }\n const properties = new Set(options.analysisKeys);\n const analysis = {valid: true, failed: {}, data: {}};\n\n properties.forEach((config) => {\n const property = config.key;\n\n analysis.data[property] = {valid: true, reports: []};\n analysis.failed[property] = [];\n let previous = {value: 0};\n let index = 0;\n\n for (let i = series.length - 1; i > 0; i -= 1) {\n const singular = {};\n\n forEach(series[i].data.getData()[config.prop], (webrtcData) => { // eslint-disable-line\n const value = webrtcData[property];\n\n if (!value || !isFinite(value)) {\n return;\n }\n singular.value = value;\n singular.difference = 0;\n singular.valid = false;\n singular.index = index;\n singular.difference = singular.value - previous.value;\n if (config.check === ANALYSIS_CHECKS.INCREASING && singular.difference > 0) {\n singular.valid = true;\n }\n else if (config.check === ANALYSIS_CHECKS.DECREASING && singular.difference < 0) {\n singular.valid = true;\n }\n else if (config.check === ANALYSIS_CHECKS.CONSTANT) {\n singular.valid = true;\n }\n else {\n singular.valid = false;\n }\n if (!singular.valid) {\n analysis.data[property].valid = false;\n analysis.valid = false;\n analysis.failed[property].push(i);\n }\n previous = singular;\n analysis.data[property].reports.push(singular);\n });\n index += 1;\n }\n if (!analysis.data[property].valid) {\n analysis.valid = false;\n }\n });\n\n return analysis;\n};\n\nexport default StatsAnalyzer;\n"],"mappings":"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"}
@@ -1,87 +0,0 @@
1
- "use strict";
2
-
3
- var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
4
-
5
- var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
6
-
7
- _Object$defineProperty(exports, "__esModule", {
8
- value: true
9
- });
10
-
11
- exports.default = void 0;
12
-
13
- var _isFinite2 = _interopRequireDefault(require("lodash/isFinite"));
14
-
15
- var _keys2 = _interopRequireDefault(require("lodash/keys"));
16
-
17
- var _constants = require("../constants");
18
-
19
- var StatsCalculator = {};
20
- /**
21
- * Calculate an interval of values between 2 data points, using updated as the "latest" so updated - previous = interval
22
- * @param {WebRTCData} previous
23
- * @param {WebRTCData} updated
24
- * @returns {Object} interval: {StringKey: IntervalValue, ..., n}
25
- * @public
26
- */
27
-
28
- StatsCalculator.difference = function (previous, updated) {
29
- // if there was no previous, just take the updated
30
- if (!previous || !previous.data || !previous.data.getData || (0, _keys2.default)(previous.data.getData()).length === 0) {
31
- return updated;
32
- }
33
-
34
- var interval = {}; // get inside the data from the filtered report
35
-
36
- (0, _keys2.default)(updated.data.getData()).forEach(function (key) {
37
- interval[key] = interval[key] ? interval[key] : {};
38
- (0, _keys2.default)(updated.data.getData()[key]).forEach(function (stat) {
39
- var value = updated.data.getData()[key][stat]; // only use some simple data points that are numbers and aren't silly things like timestamp
40
-
41
- if ((0, _isFinite2.default)(value) && !(_constants.DEFAULT_EXCLUDED_STATS.includes(stat) && value !== 0)) {
42
- // if there was nothing there before, just return the updated data
43
- if (!previous.data.getData()[key] || !previous.data.getData()[key][stat]) {
44
- interval[key][stat] = value;
45
- } // subract and store
46
- else {
47
- value -= previous.data.getData()[key][stat];
48
- interval[key][stat] = value;
49
- }
50
- }
51
- });
52
- });
53
- return interval;
54
- };
55
- /**
56
- * Calculate an aggregate of values between an old summary and a new data point, using summary as the base to add to so aggregate = summary + data
57
- * @param {WebRTCData} data
58
- * @param {Object} summary
59
- * @returns {Object} aggregate {StringKey: SummedValue, ..., n}
60
- * @public
61
- */
62
-
63
-
64
- StatsCalculator.sum = function (data, summary) {
65
- var aggregate = summary; // get inside the data from the filtered report
66
-
67
- (0, _keys2.default)(data.data.getData()).forEach(function (key) {
68
- (0, _keys2.default)(data.data.getData()[key]).forEach(function (stat) {
69
- var value = data.data.getData()[key][stat]; // only use some simple data points that are numbers and aren't silly things like timestamp
70
-
71
- if ((0, _isFinite2.default)(value) && !(_constants.DEFAULT_EXCLUDED_STATS.includes(stat) && value !== 0)) {
72
- // if there was something there before, add to that value
73
- if (aggregate[key][stat]) {
74
- aggregate[key][stat] += value;
75
- } // set up the value as the new data point
76
- else {
77
- aggregate[key][stat] = value;
78
- }
79
- }
80
- });
81
- });
82
- return aggregate;
83
- };
84
-
85
- var _default = StatsCalculator;
86
- exports.default = _default;
87
- //# sourceMappingURL=calculator.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["StatsCalculator","difference","previous","updated","data","getData","length","interval","forEach","key","stat","value","DEFAULT_EXCLUDED_STATS","includes","sum","summary","aggregate"],"sources":["calculator.js"],"sourcesContent":["import {keys, isFinite} from 'lodash';\n\nimport {DEFAULT_EXCLUDED_STATS} from '../constants';\n\nconst StatsCalculator = {};\n\n/**\n * Calculate an interval of values between 2 data points, using updated as the \"latest\" so updated - previous = interval\n * @param {WebRTCData} previous\n * @param {WebRTCData} updated\n * @returns {Object} interval: {StringKey: IntervalValue, ..., n}\n * @public\n */\nStatsCalculator.difference = (previous, updated) => {\n // if there was no previous, just take the updated\n if (!previous || !previous.data || !previous.data.getData || keys(previous.data.getData()).length === 0) {\n return updated;\n }\n const interval = {};\n\n // get inside the data from the filtered report\n keys(updated.data.getData()).forEach((key) => {\n interval[key] = interval[key] ? interval[key] : {};\n keys(updated.data.getData()[key]).forEach((stat) => {\n let value = updated.data.getData()[key][stat];\n\n // only use some simple data points that are numbers and aren't silly things like timestamp\n if (isFinite(value) && !(DEFAULT_EXCLUDED_STATS.includes(stat) && value !== 0)) {\n // if there was nothing there before, just return the updated data\n if (!previous.data.getData()[key] || !previous.data.getData()[key][stat]) {\n interval[key][stat] = value;\n }\n // subract and store\n else {\n value -= previous.data.getData()[key][stat];\n interval[key][stat] = value;\n }\n }\n });\n });\n\n return interval;\n};\n\n/**\n * Calculate an aggregate of values between an old summary and a new data point, using summary as the base to add to so aggregate = summary + data\n * @param {WebRTCData} data\n * @param {Object} summary\n * @returns {Object} aggregate {StringKey: SummedValue, ..., n}\n * @public\n */\nStatsCalculator.sum = (data, summary) => {\n const aggregate = summary;\n\n // get inside the data from the filtered report\n keys(data.data.getData()).forEach((key) => {\n keys(data.data.getData()[key]).forEach((stat) => {\n const value = data.data.getData()[key][stat];\n\n // only use some simple data points that are numbers and aren't silly things like timestamp\n if (isFinite(value) && !(DEFAULT_EXCLUDED_STATS.includes(stat) && value !== 0)) {\n // if there was something there before, add to that value\n if (aggregate[key][stat]) {\n aggregate[key][stat] += value;\n }\n // set up the value as the new data point\n else {\n aggregate[key][stat] = value;\n }\n }\n });\n });\n\n return aggregate;\n};\n\nexport default StatsCalculator;\n"],"mappings":"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"}