@webex/internal-plugin-metrics 3.0.0-beta.196 → 3.0.0-beta.197

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 (29) hide show
  1. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +5 -83
  2. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics.js +76 -3
  4. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +120 -1
  6. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  7. package/dist/call-diagnostic/generated-types-temp/ClientEvent.js.map +1 -1
  8. package/dist/call-diagnostic/generated-types-temp/Event.js.map +1 -1
  9. package/dist/metrics.js +1 -1
  10. package/dist/metrics.types.js.map +1 -1
  11. package/dist/new-metrics.js +64 -0
  12. package/dist/new-metrics.js.map +1 -1
  13. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +24 -0
  14. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +21 -0
  15. package/dist/types/call-diagnostic/generated-types-temp/ClientEvent.d.ts +135 -135
  16. package/dist/types/call-diagnostic/generated-types-temp/Event.d.ts +379 -379
  17. package/dist/types/metrics.types.d.ts +5 -0
  18. package/dist/types/new-metrics.d.ts +36 -0
  19. package/package.json +8 -8
  20. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +2 -99
  21. package/src/call-diagnostic/call-diagnostic-metrics.ts +64 -2
  22. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +132 -1
  23. package/src/call-diagnostic/generated-types-temp/ClientEvent.ts +1360 -1322
  24. package/src/call-diagnostic/generated-types-temp/Event.ts +3142 -3049
  25. package/src/metrics.types.ts +6 -0
  26. package/src/new-metrics.ts +52 -0
  27. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +145 -46
  28. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +262 -2
  29. package/test/unit/spec/new-metrics.ts +84 -22
@@ -85,3 +85,8 @@ export type SubmitMQE = (args: {
85
85
  payload: SubmitMQEPayload;
86
86
  options: any;
87
87
  }) => void;
88
+ export type BuildClientEventFetchRequestOptions = (args: {
89
+ name: ClientEvent['name'];
90
+ payload?: RecursivePartial<ClientEvent['payload']>;
91
+ options: SubmitClientEventOptions;
92
+ }) => Promise<any>;
@@ -79,5 +79,41 @@ declare class Metrics extends WebexPlugin {
79
79
  payload?: RecursivePartial<ClientEvent['payload']>;
80
80
  options: SubmitClientEventOptions;
81
81
  }): Promise<any>;
82
+ /**
83
+ * Returns a promise that will resolve to fetch options for submitting a metric.
84
+ *
85
+ * This is to support quickly submitting metrics when the browser/tab is closing.
86
+ * Calling submitClientEvent will not work because there some async steps that will
87
+ * not complete before the browser is closed. Instead, we pre-gather all the
88
+ * information/options needed for the request(s), and then simply and quickly
89
+ * fire the fetch(es) when beforeUnload is triggered.
90
+ *
91
+ * We must use fetch instead of request because fetch has a keepalive option that
92
+ * allows the request it to outlive the page.
93
+ *
94
+ * Note: the timings values will be wrong, but setMetricTimingsAndFetch() will
95
+ * properly adjust them before submitting.
96
+ *
97
+ * @public
98
+ * @param {Object} arg
99
+ * @param {String} arg.name - event name
100
+ * @param {Object} arg.payload - event payload
101
+ * @param {Object} arg.options - other options
102
+ * @returns {Promise} promise that resolves to options to be used with fetch
103
+ */
104
+ buildClientEventFetchRequestOptions({ name, payload, options, }: {
105
+ name: ClientEvent['name'];
106
+ payload?: RecursivePartial<ClientEvent['payload']>;
107
+ options: SubmitClientEventOptions;
108
+ }): Promise<any>;
109
+ /**
110
+ * Submits a metric from pre-built request options via the fetch API. Updates
111
+ * the "$timings" and "originTime" values to Date.now() since the existing times
112
+ * were set when the options were built (not submitted).
113
+
114
+ * @param {any} options - the pre-built request options for submitting a metric
115
+ * @returns {Promise} promise that resolves to the response object
116
+ */
117
+ setMetricTimingsAndFetch(options: any): Promise<any>;
82
118
  }
83
119
  export default Metrics;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/internal-plugin-metrics",
3
- "version": "3.0.0-beta.196",
3
+ "version": "3.0.0-beta.197",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -28,12 +28,12 @@
28
28
  "build": "yarn run -T tsc --declaration true --declarationDir ./dist/types"
29
29
  },
30
30
  "dependencies": {
31
- "@webex/common": "3.0.0-beta.196",
32
- "@webex/common-timers": "3.0.0-beta.196",
33
- "@webex/internal-plugin-device": "3.0.0-beta.196",
34
- "@webex/internal-plugin-metrics": "3.0.0-beta.196",
35
- "@webex/test-helper-chai": "3.0.0-beta.196",
36
- "@webex/test-helper-mock-webex": "3.0.0-beta.196",
37
- "@webex/webex-core": "3.0.0-beta.196"
31
+ "@webex/common": "3.0.0-beta.197",
32
+ "@webex/common-timers": "3.0.0-beta.197",
33
+ "@webex/internal-plugin-device": "3.0.0-beta.197",
34
+ "@webex/internal-plugin-metrics": "3.0.0-beta.197",
35
+ "@webex/test-helper-chai": "3.0.0-beta.197",
36
+ "@webex/test-helper-mock-webex": "3.0.0-beta.197",
37
+ "@webex/webex-core": "3.0.0-beta.197"
38
38
  }
39
39
  }
@@ -1,116 +1,19 @@
1
1
  /* eslint-disable class-methods-use-this */
2
2
  /* eslint-disable valid-jsdoc */
3
3
 
4
- import {isEmpty, merge} from 'lodash';
5
4
  import Batcher from '../batcher';
6
- import {
7
- ClientEvent,
8
- MetricEventNames,
9
- MediaQualityEventAudioSetupDelayPayload,
10
- MediaQualityEventVideoSetupDelayPayload,
11
- } from '../metrics.types';
5
+ import {prepareDiagnosticMetricItem} from './call-diagnostic-metrics.util';
12
6
 
13
7
  const CallDiagnosticEventsBatcher = Batcher.extend({
14
8
  namespace: 'Metrics',
15
9
 
16
- /**
17
- * @param webClientDomain
18
- * @returns
19
- */
20
- getBuildType(webClientDomain) {
21
- if (
22
- webClientDomain?.includes('localhost') ||
23
- webClientDomain?.includes('127.0.0.1') ||
24
- process.env.NODE_ENV !== 'production'
25
- ) {
26
- return 'test';
27
- }
28
-
29
- return process.env.NODE_ENV === 'production' ? 'prod' : 'test';
30
- },
31
-
32
10
  /**
33
11
  * Prepare item
34
12
  * @param item
35
13
  * @returns
36
14
  */
37
15
  prepareItem(item) {
38
- const origin = {
39
- buildType: this.getBuildType(item.event?.eventData?.webClientDomain),
40
- networkType: 'unknown',
41
- };
42
-
43
- // check event names and append latencies?
44
- const eventName = item.eventPayload?.event?.name as MetricEventNames;
45
- const joinTimes: ClientEvent['payload']['joinTimes'] = {};
46
- const audioSetupDelay: MediaQualityEventAudioSetupDelayPayload = {};
47
- const videoSetupDelay: MediaQualityEventVideoSetupDelayPayload = {};
48
-
49
- const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
50
-
51
- switch (eventName) {
52
- case 'client.interstitial-window.launched':
53
- joinTimes.meetingInfoReqResp = cdl.getMeetingInfoReqResp();
54
- joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
55
- break;
56
-
57
- case 'client.call.initiated':
58
- joinTimes.meetingInfoReqResp = cdl.getMeetingInfoReqResp();
59
- joinTimes.showInterstitialTime = cdl.getShowInterstitialTime();
60
- break;
61
-
62
- case 'client.locus.join.response':
63
- joinTimes.meetingInfoReqResp = cdl.getMeetingInfoReqResp();
64
- joinTimes.callInitJoinReq = cdl.getCallInitJoinReq();
65
- joinTimes.joinReqResp = cdl.getJoinReqResp();
66
- joinTimes.joinReqSentReceived = cdl.getJoinRespSentReceived();
67
- joinTimes.pageJmt = cdl.getPageJMT();
68
- joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
69
- joinTimes.interstitialToJoinOK = cdl.getInterstitialToJoinOK();
70
- joinTimes.totalJmt = cdl.getTotalJMT();
71
- joinTimes.clientJmt = cdl.getClientJMT();
72
- break;
73
-
74
- case 'client.ice.end':
75
- joinTimes.ICESetupTime = cdl.getICESetupTime();
76
- joinTimes.audioICESetupTime = cdl.getAudioICESetupTime();
77
- joinTimes.videoICESetupTime = cdl.getVideoICESetupTime();
78
- joinTimes.shareICESetupTime = cdl.getShareICESetupTime();
79
- break;
80
-
81
- case 'client.media.rx.start':
82
- joinTimes.localSDPGenRemoteSDPRecv = cdl.getLocalSDPGenRemoteSDPRecv();
83
- break;
84
-
85
- case 'client.media-engine.ready':
86
- joinTimes.totalMediaJMT = cdl.getTotalMediaJMT();
87
- joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
88
- joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
89
- joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
90
- break;
91
-
92
- case 'client.mediaquality.event':
93
- audioSetupDelay.joinRespRxStart = cdl.getAudioJoinRespRxStart();
94
- audioSetupDelay.joinRespTxStart = cdl.getAudioJoinRespTxStart();
95
- videoSetupDelay.joinRespRxStart = cdl.getVideoJoinRespRxStart();
96
- videoSetupDelay.joinRespTxStart = cdl.getVideoJoinRespTxStart();
97
- }
98
-
99
- if (!isEmpty(joinTimes)) {
100
- item.eventPayload.event = merge(item.eventPayload.event, {joinTimes});
101
- }
102
-
103
- if (!isEmpty(audioSetupDelay)) {
104
- item.eventPayload.event = merge(item.eventPayload.event, {audioSetupDelay});
105
- }
106
-
107
- if (!isEmpty(videoSetupDelay)) {
108
- item.eventPayload.event = merge(item.eventPayload.event, {videoSetupDelay});
109
- }
110
-
111
- item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
112
-
113
- return Promise.resolve(item);
16
+ return Promise.resolve(prepareDiagnosticMetricItem(this.webex, item));
114
17
  },
115
18
 
116
19
  /**
@@ -11,6 +11,7 @@ import {
11
11
  anonymizeIPAddress,
12
12
  clearEmptyKeysRecursively,
13
13
  isLocusServiceErrorCode,
14
+ prepareDiagnosticMetricItem,
14
15
  userAgentToString,
15
16
  } from './call-diagnostic-metrics.util';
16
17
  import {CLIENT_NAME} from '../config';
@@ -474,14 +475,15 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
474
475
  }
475
476
 
476
477
  /**
477
- * Submit Client Event CA event.
478
+ * Prepare Client Event CA event.
478
479
  * @param arg - submit params
479
480
  * @param arg.event - event key
480
481
  * @param arg.payload - additional payload to be merged with default payload
481
482
  * @param arg.options - payload
483
+ * @returns {any} options to be with fetch
482
484
  * @throws
483
485
  */
484
- public submitClientEvent({
486
+ private prepareClientEvent({
485
487
  name,
486
488
  payload,
487
489
  options,
@@ -509,6 +511,28 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
509
511
  // append client event data to the call diagnostic event
510
512
  const diagnosticEvent = this.prepareDiagnosticEvent(clientEventObject, options);
511
513
 
514
+ return diagnosticEvent;
515
+ }
516
+
517
+ /**
518
+ * Submit Client Event CA event.
519
+ * @param arg - submit params
520
+ * @param arg.event - event key
521
+ * @param arg.payload - additional payload to be merged with default payload
522
+ * @param arg.options - payload
523
+ * @throws
524
+ */
525
+ public submitClientEvent({
526
+ name,
527
+ payload,
528
+ options,
529
+ }: {
530
+ name: ClientEvent['name'];
531
+ payload?: ClientEventPayload;
532
+ options: SubmitClientEventOptions;
533
+ }) {
534
+ const diagnosticEvent = this.prepareClientEvent({name, payload, options});
535
+
512
536
  return this.submitToCallDiagnostics(diagnosticEvent);
513
537
  }
514
538
 
@@ -526,4 +550,42 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
526
550
 
527
551
  return this.callDiagnosticEventsBatcher.request(finalEvent);
528
552
  }
553
+
554
+ /**
555
+ * Builds a request options object to later be passed to fetch().
556
+ * @param arg - submit params
557
+ * @param arg.event - event key
558
+ * @param arg.payload - additional payload to be merged with default payload
559
+ * @param arg.options - client event options
560
+ * @returns {Promise<any>}
561
+ * @throws
562
+ */
563
+ public async buildClientEventFetchRequestOptions({
564
+ name,
565
+ payload,
566
+ options,
567
+ }: {
568
+ name: ClientEvent['name'];
569
+ payload?: ClientEventPayload;
570
+ options: SubmitClientEventOptions;
571
+ }): Promise<any> {
572
+ const clientEvent = this.prepareClientEvent({name, payload, options});
573
+
574
+ // build metrics-a event type
575
+ // @ts-ignore
576
+ const diagnosticEvent = prepareDiagnosticMetricItem(this.webex, {
577
+ eventPayload: clientEvent,
578
+ type: ['diagnostic-event'],
579
+ });
580
+
581
+ // @ts-ignore
582
+ return this.webex.prepareFetchOptions({
583
+ method: 'POST',
584
+ service: 'metrics',
585
+ resource: 'clientmetrics',
586
+ body: {
587
+ metrics: [diagnosticEvent],
588
+ },
589
+ });
590
+ }
529
591
  }
@@ -3,7 +3,13 @@ import anonymize from 'ip-anonymize';
3
3
  import util from 'util';
4
4
 
5
5
  import {BrowserDetection} from '@webex/common';
6
- import {isEmpty} from 'lodash';
6
+ import {isEmpty, merge} from 'lodash';
7
+ import {
8
+ ClientEvent,
9
+ MediaQualityEventAudioSetupDelayPayload,
10
+ MediaQualityEventVideoSetupDelayPayload,
11
+ MetricEventNames,
12
+ } from '../metrics.types';
7
13
 
8
14
  const {getOSName, getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
9
15
 
@@ -100,3 +106,128 @@ export const isLocusServiceErrorCode = (errorCode: string | number) => {
100
106
 
101
107
  return false;
102
108
  };
109
+
110
+ /**
111
+ * @param webClientDomain
112
+ * @returns
113
+ */
114
+ export const getBuildType = (webClientDomain) => {
115
+ if (
116
+ webClientDomain?.includes('localhost') ||
117
+ webClientDomain?.includes('127.0.0.1') ||
118
+ process.env.NODE_ENV !== 'production'
119
+ ) {
120
+ return 'test';
121
+ }
122
+
123
+ return 'production';
124
+ };
125
+
126
+ /**
127
+ * Prepare metric item for submission.
128
+ * @param {Object} webex sdk instance
129
+ * @param {Object} item
130
+ * @returns {Object} prepared item
131
+ */
132
+ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
133
+ const origin = {
134
+ buildType: getBuildType(item.event?.eventData?.webClientDomain),
135
+ networkType: 'unknown',
136
+ };
137
+
138
+ // check event names and append latencies?
139
+ const eventName = item.eventPayload?.event?.name as MetricEventNames;
140
+ const joinTimes: ClientEvent['payload']['joinTimes'] = {};
141
+ const audioSetupDelay: MediaQualityEventAudioSetupDelayPayload = {};
142
+ const videoSetupDelay: MediaQualityEventVideoSetupDelayPayload = {};
143
+
144
+ const cdl = webex.internal.newMetrics.callDiagnosticLatencies;
145
+
146
+ switch (eventName) {
147
+ case 'client.interstitial-window.launched':
148
+ joinTimes.meetingInfoReqResp = cdl.getMeetingInfoReqResp();
149
+ joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
150
+ break;
151
+
152
+ case 'client.call.initiated':
153
+ joinTimes.meetingInfoReqResp = cdl.getMeetingInfoReqResp();
154
+ joinTimes.showInterstitialTime = cdl.getShowInterstitialTime();
155
+ break;
156
+
157
+ case 'client.locus.join.response':
158
+ joinTimes.meetingInfoReqResp = cdl.getMeetingInfoReqResp();
159
+ joinTimes.callInitJoinReq = cdl.getCallInitJoinReq();
160
+ joinTimes.joinReqResp = cdl.getJoinReqResp();
161
+ joinTimes.joinReqSentReceived = cdl.getJoinRespSentReceived();
162
+ joinTimes.pageJmt = cdl.getPageJMT();
163
+ joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
164
+ joinTimes.interstitialToJoinOK = cdl.getInterstitialToJoinOK();
165
+ joinTimes.totalJmt = cdl.getTotalJMT();
166
+ joinTimes.clientJmt = cdl.getClientJMT();
167
+ break;
168
+
169
+ case 'client.ice.end':
170
+ joinTimes.ICESetupTime = cdl.getICESetupTime();
171
+ joinTimes.audioICESetupTime = cdl.getAudioICESetupTime();
172
+ joinTimes.videoICESetupTime = cdl.getVideoICESetupTime();
173
+ joinTimes.shareICESetupTime = cdl.getShareICESetupTime();
174
+ break;
175
+
176
+ case 'client.media.rx.start':
177
+ joinTimes.localSDPGenRemoteSDPRecv = cdl.getLocalSDPGenRemoteSDPRecv();
178
+ break;
179
+
180
+ case 'client.media-engine.ready':
181
+ joinTimes.totalMediaJMT = cdl.getTotalMediaJMT();
182
+ joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
183
+ joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
184
+ joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
185
+ break;
186
+
187
+ case 'client.mediaquality.event':
188
+ audioSetupDelay.joinRespRxStart = cdl.getAudioJoinRespRxStart();
189
+ audioSetupDelay.joinRespTxStart = cdl.getAudioJoinRespTxStart();
190
+ videoSetupDelay.joinRespRxStart = cdl.getVideoJoinRespRxStart();
191
+ videoSetupDelay.joinRespTxStart = cdl.getVideoJoinRespTxStart();
192
+ }
193
+
194
+ if (!isEmpty(joinTimes)) {
195
+ item.eventPayload.event = merge(item.eventPayload.event, {joinTimes});
196
+ }
197
+
198
+ if (!isEmpty(audioSetupDelay)) {
199
+ item.eventPayload.event = merge(item.eventPayload.event, {audioSetupDelay});
200
+ }
201
+
202
+ if (!isEmpty(videoSetupDelay)) {
203
+ item.eventPayload.event = merge(item.eventPayload.event, {videoSetupDelay});
204
+ }
205
+
206
+ item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
207
+
208
+ return item;
209
+ };
210
+
211
+ /**
212
+ * Sets the originTime value(s) before the request/fetch.
213
+ * This function is only useful if you are about to submit a metrics
214
+ * request using pre-built fetch options;
215
+ *
216
+ * @param {any} options
217
+ * @returns {any} the updated options object
218
+ */
219
+ export const setMetricTimings = (options) => {
220
+ if (options.body?.metrics) {
221
+ const now = new Date().toISOString();
222
+ options.body.metrics.forEach((metric) => {
223
+ if (metric.eventPayload) {
224
+ metric.eventPayload.originTime = {
225
+ triggered: now,
226
+ sent: now,
227
+ };
228
+ }
229
+ });
230
+ }
231
+
232
+ return options;
233
+ };