@webex/internal-plugin-metrics 3.0.0-beta.3 → 3.0.0-beta.300

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 (69) hide show
  1. package/README.md +1 -3
  2. package/dist/batcher.js +5 -23
  3. package/dist/batcher.js.map +1 -1
  4. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +66 -0
  5. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +456 -0
  7. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -0
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js +798 -0
  9. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +337 -0
  11. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -0
  12. package/dist/call-diagnostic/config.js +604 -0
  13. package/dist/call-diagnostic/config.js.map +1 -0
  14. package/dist/client-metrics-batcher.js +3 -8
  15. package/dist/client-metrics-batcher.js.map +1 -1
  16. package/dist/config.js +23 -6
  17. package/dist/config.js.map +1 -1
  18. package/dist/index.js +31 -10
  19. package/dist/index.js.map +1 -1
  20. package/dist/metrics.js +43 -80
  21. package/dist/metrics.js.map +1 -1
  22. package/dist/metrics.types.js +7 -0
  23. package/dist/metrics.types.js.map +1 -0
  24. package/dist/new-metrics.js +333 -0
  25. package/dist/new-metrics.js.map +1 -0
  26. package/dist/types/batcher.d.ts +2 -0
  27. package/dist/types/call-diagnostic/call-diagnostic-metrics-batcher.d.ts +2 -0
  28. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +194 -0
  29. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +405 -0
  30. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +96 -0
  31. package/dist/types/call-diagnostic/config.d.ts +171 -0
  32. package/dist/types/client-metrics-batcher.d.ts +2 -0
  33. package/dist/types/config.d.ts +36 -0
  34. package/dist/types/index.d.ts +13 -0
  35. package/dist/types/metrics.d.ts +3 -0
  36. package/dist/types/metrics.types.d.ts +103 -0
  37. package/dist/types/new-metrics.d.ts +139 -0
  38. package/dist/types/utils.d.ts +6 -0
  39. package/dist/utils.js +27 -0
  40. package/dist/utils.js.map +1 -0
  41. package/package.json +13 -8
  42. package/src/batcher.js +34 -26
  43. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +83 -0
  44. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +414 -0
  45. package/src/call-diagnostic/call-diagnostic-metrics.ts +863 -0
  46. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +362 -0
  47. package/src/call-diagnostic/config.ts +660 -0
  48. package/src/client-metrics-batcher.js +4 -4
  49. package/src/config.js +26 -5
  50. package/src/index.ts +43 -0
  51. package/src/metrics.js +44 -58
  52. package/src/metrics.types.ts +159 -0
  53. package/src/new-metrics.ts +317 -0
  54. package/src/utils.ts +17 -0
  55. package/test/unit/spec/batcher.js +28 -15
  56. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +465 -0
  57. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +477 -0
  58. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +1943 -0
  59. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +565 -0
  60. package/test/unit/spec/client-metrics-batcher.js +28 -15
  61. package/test/unit/spec/metrics.js +86 -116
  62. package/test/unit/spec/new-metrics.ts +269 -0
  63. package/test/unit/spec/utils.ts +22 -0
  64. package/tsconfig.json +6 -0
  65. package/dist/call-diagnostic-events-batcher.js +0 -70
  66. package/dist/call-diagnostic-events-batcher.js.map +0 -1
  67. package/src/call-diagnostic-events-batcher.js +0 -62
  68. package/src/index.js +0 -17
  69. package/test/unit/spec/call-diagnostic-events-batcher.js +0 -180
@@ -0,0 +1,317 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /* eslint-disable class-methods-use-this */
3
+ /* eslint-disable valid-jsdoc */
4
+
5
+ // @ts-ignore
6
+ import {WebexPlugin} from '@webex/webex-core';
7
+
8
+ import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
9
+ import {
10
+ RecursivePartial,
11
+ ClientEvent,
12
+ FeatureEvent,
13
+ BehavioralEvent,
14
+ OperationalEvent,
15
+ MediaQualityEvent,
16
+ InternalEvent,
17
+ SubmitClientEventOptions,
18
+ } from './metrics.types';
19
+ import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
20
+ import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
21
+ import {generateCommonErrorMetadata} from './utils';
22
+
23
+ /**
24
+ * Metrics plugin to centralize all types of metrics.
25
+ * @class
26
+ */
27
+ class Metrics extends WebexPlugin {
28
+ // eslint-disable-next-line no-use-before-define
29
+ static instance: Metrics;
30
+
31
+ // Call Diagnostic latencies
32
+ callDiagnosticLatencies: CallDiagnosticLatencies;
33
+ // Helper classes to handle the different types of metrics
34
+ callDiagnosticMetrics: CallDiagnosticMetrics;
35
+
36
+ /**
37
+ * Constructor
38
+ * @param args
39
+ * @constructor
40
+ * @private
41
+ * @returns
42
+ */
43
+ constructor(...args) {
44
+ super(...args);
45
+
46
+ this.onReady();
47
+ }
48
+
49
+ /**
50
+ * On Ready
51
+ */
52
+ private onReady() {
53
+ // @ts-ignore
54
+ this.webex.once('ready', () => {
55
+ // @ts-ignore
56
+ this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
57
+ // @ts-ignore
58
+ this.callDiagnosticLatencies = new CallDiagnosticLatencies({}, {parent: this.webex});
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Used for internal purposes only
64
+ * @param args
65
+ */
66
+ submitInternalEvent({
67
+ name,
68
+ payload,
69
+ options,
70
+ }: {
71
+ name: InternalEvent['name'];
72
+ payload?: RecursivePartial<InternalEvent['payload']>;
73
+ options?: any;
74
+ }) {
75
+ if (name === 'internal.reset.join.latencies') {
76
+ this.callDiagnosticLatencies.clearTimestamps();
77
+ } else {
78
+ this.callDiagnosticLatencies.saveTimestamp({key: name});
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Behavioral event
84
+ * @param args
85
+ */
86
+ submitBehavioralEvent({
87
+ name,
88
+ payload,
89
+ options,
90
+ }: {
91
+ name: BehavioralEvent['name'];
92
+ payload?: RecursivePartial<BehavioralEvent['payload']>;
93
+ options?: any;
94
+ }) {
95
+ this.callDiagnosticLatencies.saveTimestamp({key: name});
96
+ throw new Error('Not implemented.');
97
+ }
98
+
99
+ /**
100
+ * Operational event
101
+ * @param args
102
+ */
103
+ submitOperationalEvent({
104
+ name,
105
+ payload,
106
+ options,
107
+ }: {
108
+ name: OperationalEvent['name'];
109
+ payload?: RecursivePartial<OperationalEvent['payload']>;
110
+ options?: any;
111
+ }) {
112
+ throw new Error('Not implemented.');
113
+ }
114
+
115
+ /**
116
+ * Call Analyzer: Media Quality Event
117
+ * @param args
118
+ */
119
+ submitMQE({
120
+ name,
121
+ payload,
122
+ options,
123
+ }: {
124
+ name: MediaQualityEvent['name'];
125
+ payload: RecursivePartial<MediaQualityEvent['payload']> & {
126
+ intervals: MediaQualityEvent['payload']['intervals'];
127
+ };
128
+ options: any;
129
+ }) {
130
+ this.callDiagnosticLatencies.saveTimestamp({key: name});
131
+ this.callDiagnosticMetrics.submitMQE({name, payload, options});
132
+ }
133
+
134
+ /**
135
+ * Call Analyzer: Feature Usage Event
136
+ * @param args
137
+ */
138
+ submitFeatureEvent({
139
+ name,
140
+ payload,
141
+ options,
142
+ }: {
143
+ name: FeatureEvent['name'];
144
+ payload?: RecursivePartial<FeatureEvent['payload']>;
145
+ options: any;
146
+ }) {
147
+ throw new Error('Not implemented.');
148
+ }
149
+
150
+ /**
151
+ * Call Analyzer: Client Event
152
+ * @public
153
+ * @param args
154
+ */
155
+ public submitClientEvent({
156
+ name,
157
+ payload,
158
+ options,
159
+ }: {
160
+ name: ClientEvent['name'];
161
+ payload?: RecursivePartial<ClientEvent['payload']>;
162
+ options?: SubmitClientEventOptions;
163
+ }): Promise<any> {
164
+ if (!this.callDiagnosticLatencies || !this.callDiagnosticMetrics) {
165
+ // @ts-ignore
166
+ this.webex.logger.log(
167
+ `NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: ${name}`
168
+ );
169
+
170
+ return Promise.resolve();
171
+ }
172
+ this.callDiagnosticLatencies.saveTimestamp({
173
+ key: name,
174
+ options: {meetingId: options?.meetingId},
175
+ });
176
+
177
+ return this.callDiagnosticMetrics.submitClientEvent({name, payload, options});
178
+ }
179
+
180
+ /**
181
+ * Submit a pre-login metric to clientmetrics
182
+ * @public
183
+ * @param payload
184
+ * @param preLoginId - pre-login ID of user
185
+ * @returns
186
+ */
187
+ public postPreLoginMetric(payload: any, preLoginId: string): Promise<any> {
188
+ // @ts-ignore
189
+ return this.webex
190
+ .request({
191
+ method: 'POST',
192
+ api: 'metrics',
193
+ resource: 'clientmetrics-prelogin',
194
+ headers: {
195
+ authorization: false,
196
+ 'x-prelogin-userid': preLoginId,
197
+ },
198
+ body: {
199
+ metrics: [payload],
200
+ },
201
+ })
202
+ .then((res) => {
203
+ // @ts-ignore
204
+ this.webex.logger.log(`NewMetrics: @postPreLoginMetric. Request successful:`, res);
205
+
206
+ return res;
207
+ })
208
+ .catch((err) => {
209
+ // @ts-ignore
210
+ this.logger.error(
211
+ `NewMetrics: @postPreLoginMetric. Request failed:`,
212
+ `err: ${generateCommonErrorMetadata(err)}`
213
+ );
214
+
215
+ return Promise.reject(err);
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Issue request to alias a user's pre-login ID with their CI UUID
221
+ * @param {string} preLoginId
222
+ * @returns {Object} HttpResponse object
223
+ */
224
+ public clientMetricsAliasUser(preLoginId: string) {
225
+ // @ts-ignore
226
+ return this.webex
227
+ .request({
228
+ method: 'POST',
229
+ api: 'metrics',
230
+ resource: 'clientmetrics',
231
+ headers: {
232
+ 'x-prelogin-userid': preLoginId,
233
+ },
234
+ body: {},
235
+ qs: {
236
+ alias: true,
237
+ },
238
+ })
239
+ .then((res) => {
240
+ // @ts-ignore
241
+ this.webex.logger.log(`NewMetrics: @clientMetricsAliasUser. Request successful:`, res);
242
+
243
+ return res;
244
+ })
245
+ .catch((err) => {
246
+ // @ts-ignore
247
+ this.logger.error(
248
+ `NewMetrics: @clientMetricsAliasUser. Request failed:`,
249
+ `err: ${generateCommonErrorMetadata(err)}`
250
+ );
251
+
252
+ return Promise.reject(err);
253
+ });
254
+ }
255
+
256
+ /**
257
+ * Returns a promise that will resolve to fetch options for submitting a metric.
258
+ *
259
+ * This is to support quickly submitting metrics when the browser/tab is closing.
260
+ * Calling submitClientEvent will not work because there some async steps that will
261
+ * not complete before the browser is closed. Instead, we pre-gather all the
262
+ * information/options needed for the request(s), and then simply and quickly
263
+ * fire the fetch(es) when beforeUnload is triggered.
264
+ *
265
+ * We must use fetch instead of request because fetch has a keepalive option that
266
+ * allows the request it to outlive the page.
267
+ *
268
+ * Note: the timings values will be wrong, but setMetricTimingsAndFetch() will
269
+ * properly adjust them before submitting.
270
+ *
271
+ * @public
272
+ * @param {Object} arg
273
+ * @param {String} arg.name - event name
274
+ * @param {Object} arg.payload - event payload
275
+ * @param {Object} arg.options - other options
276
+ * @returns {Promise} promise that resolves to options to be used with fetch
277
+ */
278
+ public async buildClientEventFetchRequestOptions({
279
+ name,
280
+ payload,
281
+ options,
282
+ }: {
283
+ name: ClientEvent['name'];
284
+ payload?: RecursivePartial<ClientEvent['payload']>;
285
+ options?: SubmitClientEventOptions;
286
+ }): Promise<any> {
287
+ return this.callDiagnosticMetrics.buildClientEventFetchRequestOptions({
288
+ name,
289
+ payload,
290
+ options,
291
+ });
292
+ }
293
+
294
+ /**
295
+ * Submits a metric from pre-built request options via the fetch API. Updates
296
+ * the "$timings" and "originTime" values to Date.now() since the existing times
297
+ * were set when the options were built (not submitted).
298
+
299
+ * @param {any} options - the pre-built request options for submitting a metric
300
+ * @returns {Promise} promise that resolves to the response object
301
+ */
302
+ public setMetricTimingsAndFetch(options: any): Promise<any> {
303
+ // @ts-ignore
304
+ return this.webex.setTimingsAndFetch(setMetricTimings(options));
305
+ }
306
+
307
+ /**
308
+ * Returns true if the specified serviceErrorCode maps to an expected error.
309
+ * @param {number} serviceErrorCode the service error code
310
+ * @returns {boolean}
311
+ */
312
+ public isServiceErrorExpected(serviceErrorCode: number): boolean {
313
+ return this.callDiagnosticMetrics.isServiceErrorExpected(serviceErrorCode);
314
+ }
315
+ }
316
+
317
+ export default Metrics;
package/src/utils.ts ADDED
@@ -0,0 +1,17 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ /**
3
+ * Generates common metadata for errors
4
+ * @param {any} error
5
+ * @returns {object}
6
+ */
7
+ export const generateCommonErrorMetadata = (error) => {
8
+ if (error instanceof Error) {
9
+ return JSON.stringify({
10
+ message: error?.message,
11
+ name: error?.name,
12
+ stack: error?.stack,
13
+ });
14
+ }
15
+
16
+ return error;
17
+ };
@@ -27,8 +27,8 @@ describe('plugin-metrics', () => {
27
27
  beforeEach(() => {
28
28
  webex = new MockWebex({
29
29
  children: {
30
- metrics: Metrics
31
- }
30
+ metrics: Metrics,
31
+ },
32
32
  });
33
33
 
34
34
  webex.config.metrics = config.metrics;
@@ -37,7 +37,8 @@ describe('plugin-metrics', () => {
37
37
  return Promise.resolve({
38
38
  statusCode: 204,
39
39
  body: undefined,
40
- options
40
+ waitForServiceTimeout: 15,
41
+ options,
41
42
  });
42
43
  };
43
44
  sinon.spy(webex, 'request');
@@ -58,9 +59,10 @@ describe('plugin-metrics', () => {
58
59
  it('clears the queue', () => {
59
60
  clock.uninstall();
60
61
 
61
- return webex.internal.metrics.batcher.request({
62
- key: 'testMetric'
63
- })
62
+ return webex.internal.metrics.batcher
63
+ .request({
64
+ key: 'testMetric',
65
+ })
64
66
  .then(() => {
65
67
  assert.calledOnce(webex.request);
66
68
  assert.lengthOf(webex.internal.metrics.batcher.queue, 0);
@@ -80,26 +82,29 @@ describe('plugin-metrics', () => {
80
82
 
81
83
  sinon.stub(webex, 'request').callsFake((options) => {
82
84
  options.headers = {
83
- trackingid: count
85
+ trackingid: count,
84
86
  };
85
87
 
86
88
  count += 1;
87
89
  if (count < 9) {
88
- return Promise.reject(new WebexHttpError.NetworkOrCORSError({
89
- statusCode: 0,
90
- options
91
- }));
90
+ return Promise.reject(
91
+ new WebexHttpError.NetworkOrCORSError({
92
+ statusCode: 0,
93
+ options,
94
+ })
95
+ );
92
96
  }
93
97
 
94
98
  return Promise.resolve({
95
99
  statusCode: 204,
96
100
  body: undefined,
97
- options
101
+ waitForServiceTimeout: 15,
102
+ options,
98
103
  });
99
104
  });
100
105
 
101
106
  const promise = webex.internal.metrics.batcher.request({
102
- key: 'testMetric'
107
+ key: 'testMetric',
103
108
  });
104
109
 
105
110
  return promiseTick(50)
@@ -159,8 +164,16 @@ describe('plugin-metrics', () => {
159
164
  .then(() => assert.lengthOf(webex.internal.metrics.batcher.queue, 0))
160
165
  .then(() => promise)
161
166
  .then(() => {
162
- assert.lengthOf(webex.request.args[1][0].body.metrics, 1, 'Reenqueuing the metric once did not increase the number of metrics to be submitted');
163
- assert.lengthOf(webex.request.args[2][0].body.metrics, 1, 'Reenqueuing the metric twice did not increase the number of metrics to be submitted');
167
+ assert.lengthOf(
168
+ webex.request.args[1][0].body.metrics,
169
+ 1,
170
+ 'Reenqueuing the metric once did not increase the number of metrics to be submitted'
171
+ );
172
+ assert.lengthOf(
173
+ webex.request.args[2][0].body.metrics,
174
+ 1,
175
+ 'Reenqueuing the metric twice did not increase the number of metrics to be submitted'
176
+ );
164
177
  assert.lengthOf(webex.internal.metrics.batcher.queue, 0);
165
178
  });
166
179
  });