@webex/internal-plugin-metrics 3.12.0-task-refactor.1 → 3.12.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 (56) hide show
  1. package/dist/batcher.js +3 -0
  2. package/dist/batcher.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +23 -0
  4. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +68 -50
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  7. package/dist/call-diagnostic/call-diagnostic-metrics.js +60 -8
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  9. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +44 -4
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  11. package/dist/call-diagnostic/config.js +17 -3
  12. package/dist/call-diagnostic/config.js.map +1 -1
  13. package/dist/config.js +1 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/generic-metrics.js +8 -6
  16. package/dist/generic-metrics.js.map +1 -1
  17. package/dist/index.js +7 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/metrics.js +1 -1
  20. package/dist/metrics.types.js.map +1 -1
  21. package/dist/new-metrics.js +56 -20
  22. package/dist/new-metrics.js.map +1 -1
  23. package/dist/prelogin-metrics-batcher.js +23 -0
  24. package/dist/prelogin-metrics-batcher.js.map +1 -1
  25. package/dist/prelogin-metrics.js +106 -0
  26. package/dist/prelogin-metrics.js.map +1 -0
  27. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +9 -0
  28. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +57 -20
  29. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +23 -2
  30. package/dist/types/call-diagnostic/config.d.ts +6 -19
  31. package/dist/types/config.d.ts +16 -15
  32. package/dist/types/index.d.ts +2 -1
  33. package/dist/types/metrics.types.d.ts +4 -4
  34. package/dist/types/new-metrics.d.ts +12 -0
  35. package/dist/types/prelogin-metrics.d.ts +47 -0
  36. package/package.json +11 -11
  37. package/src/batcher.js +4 -0
  38. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +26 -0
  39. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +139 -70
  40. package/src/call-diagnostic/call-diagnostic-metrics.ts +52 -1
  41. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +49 -2
  42. package/src/call-diagnostic/config.ts +15 -0
  43. package/src/config.js +1 -0
  44. package/src/generic-metrics.ts +6 -6
  45. package/src/index.ts +2 -0
  46. package/src/metrics.types.ts +3 -3
  47. package/src/new-metrics.ts +42 -0
  48. package/src/prelogin-metrics-batcher.ts +26 -0
  49. package/src/prelogin-metrics.ts +94 -0
  50. package/test/unit/spec/batcher.js +43 -0
  51. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +183 -11
  52. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +324 -366
  53. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +724 -159
  54. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +92 -3
  55. package/test/unit/spec/prelogin-metrics-batcher.ts +190 -36
  56. package/test/unit/spec/prelogin-metrics.ts +132 -0
@@ -26,6 +26,7 @@ export const WEBEX_SUB_SERVICE_TYPES: Record<string, ClientSubServiceType> = {
26
26
  SCHEDULED_MEETING: 'ScheduledMeeting',
27
27
  WEBINAR: 'Webinar',
28
28
  WEBCAST: 'Webcast',
29
+ LARGE_SCALE_WEBINAR: 'LargeScaleWebinar',
29
30
  };
30
31
 
31
32
  // Found in https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
@@ -133,8 +134,10 @@ export const ERROR_DESCRIPTIONS = {
133
134
  MULTISTREAM_NOT_AVAILABLE: 'MultistreamNotAvailable',
134
135
  SDP_OFFER_CREATION_ERROR: 'SdpOfferCreationError',
135
136
  SDP_OFFER_CREATION_ERROR_MISSING_CODEC: 'SdpOfferCreationErrorMissingCodec',
137
+ WEBRTC_API_NOT_AVAILABLE: 'WebrtcApiNotAvailableError',
136
138
  WDM_RESTRICTED_REGION: 'WdmRestrictedRegion',
137
139
  USER_NOT_ALLOWED_JOIN_WEBINAR: 'UserNotAllowedJoinWebinar',
140
+ INVALID_MEETING_INFO: 'InvalidMeetingInfo',
138
141
  };
139
142
 
140
143
  export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
@@ -145,6 +148,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
145
148
  99002: 4100,
146
149
  // Cannot find the data. Unkown meeting.
147
150
  99009: 4100,
151
+ // The input parameters contain invalid item
152
+ 99019: 4105,
148
153
  // Meeting is not allow to cross env
149
154
  58500: 4100,
150
155
  // Input parameters contain invalit item
@@ -200,6 +205,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
200
205
  423005: 4005,
201
206
  // Wrong password or host key with too many requests
202
207
  423006: 4005,
208
+ // PanelistPasswordError too many time,please input captcha code
209
+ 423008: 4005,
203
210
  // PasswordError with right captcha, please input captcha code
204
211
  423010: 4005,
205
212
  // PasswordOrHostKeyError with right captcha, please input captcha code
@@ -226,6 +233,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
226
233
  403003: 4101,
227
234
  // Attendee email is required
228
235
  403030: 4101,
236
+ // webinar need login when un-invited attendee join
237
+ 403106: 4104,
229
238
 
230
239
  // ---- Locus ------
231
240
  // FREE_USER_MAX_PARTICIPANTS_EXCEEDED
@@ -695,6 +704,12 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
695
704
  category: 'expected',
696
705
  fatal: true,
697
706
  },
707
+ 4105: {
708
+ errorDescription: ERROR_DESCRIPTIONS.INVALID_MEETING_INFO,
709
+ category: 'expected',
710
+ fatal: false,
711
+ shownToUser: true,
712
+ },
698
713
  2729: {
699
714
  errorDescription: ERROR_DESCRIPTIONS.NO_MEDIA_FOUND,
700
715
  category: 'expected',
package/src/config.js CHANGED
@@ -19,6 +19,7 @@ export default {
19
19
  batcherMaxCalls: 50,
20
20
  batcherMaxWait: 1500,
21
21
  batcherRetryPlateau: 32000,
22
+ batcherRetryOnNetworkError: true,
22
23
  waitForServiceTimeout: 30,
23
24
  },
24
25
  };
@@ -79,7 +79,7 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
79
79
  device: {
80
80
  id: this.getDeviceId(),
81
81
  },
82
- locale: window.navigator.language,
82
+ locale: window?.navigator.language,
83
83
  os: {
84
84
  name: getOSNameInternal(),
85
85
  version: getOSVersion(),
@@ -94,12 +94,12 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
94
94
  protected getBrowserDetails(): object {
95
95
  return {
96
96
  browser: getBrowserName(),
97
- browserHeight: window.innerHeight,
97
+ browserHeight: window?.innerHeight,
98
98
  browserVersion: getBrowserVersion(),
99
- browserWidth: window.innerWidth,
100
- domain: window.location.hostname,
101
- inIframe: window.self !== window.top,
102
- locale: window.navigator.language,
99
+ browserWidth: window?.innerWidth,
100
+ domain: window?.location.hostname,
101
+ inIframe: window?.self !== window?.top,
102
+ locale: window?.navigator.language,
103
103
  os: getOSNameInternal(),
104
104
  };
105
105
  }
package/src/index.ts CHANGED
@@ -28,6 +28,7 @@ import BehavioralMetrics from './behavioral-metrics';
28
28
  import OperationalMetrics from './operational-metrics';
29
29
  import BusinessMetrics from './business-metrics';
30
30
  import RtcMetrics from './rtcMetrics';
31
+ import PreLoginMetrics from './prelogin-metrics';
31
32
 
32
33
  registerInternalPlugin('metrics', Metrics, {
33
34
  config,
@@ -51,6 +52,7 @@ export {
51
52
  OperationalMetrics,
52
53
  BusinessMetrics,
53
54
  RtcMetrics,
55
+ PreLoginMetrics,
54
56
  };
55
57
  export type {
56
58
  ClientEvent,
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  ClientEvent as RawClientEvent,
3
3
  Event as RawEvent,
4
4
  MediaQualityEvent as RawMediaQualityEvent,
@@ -156,7 +156,6 @@ export type InternalEvent = {
156
156
  | 'internal.register.device.request'
157
157
  | 'internal.register.device.response'
158
158
  | 'internal.reset.join.latencies'
159
- | 'internal.client.meeting.click.joinbutton'
160
159
  | 'internal.host.meeting.participant.admitted'
161
160
  | 'internal.client.meeting.interstitial-window.showed'
162
161
  | 'internal.client.interstitial-window.click.joinbutton'
@@ -197,7 +196,7 @@ export interface BusinessEventPayload {
197
196
  metricName: string;
198
197
  timestamp: number;
199
198
  context: DeviceContext;
200
- browserDetails: EventPayload;
199
+ browserDetails: object;
201
200
  value: EventPayload;
202
201
  }
203
202
 
@@ -319,6 +318,7 @@ export type PreComputedLatencies =
319
318
  | 'internal.get.cluster.time'
320
319
  | 'internal.click.to.interstitial'
321
320
  | 'internal.click.to.interstitial.with.user.delay'
321
+ | 'internal.click.to.interstitial.for.client.jmt'
322
322
  | 'internal.refresh.captcha.time'
323
323
  | 'internal.exchange.ci.token.time'
324
324
  | 'internal.get.u2c.time'
@@ -9,6 +9,8 @@ import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
9
9
  import BehavioralMetrics from './behavioral-metrics';
10
10
  import OperationalMetrics from './operational-metrics';
11
11
  import BusinessMetrics from './business-metrics';
12
+ import PreLoginMetrics from './prelogin-metrics';
13
+ import PreLoginMetricsBatcher from './prelogin-metrics-batcher';
12
14
  import {
13
15
  RecursivePartial,
14
16
  MetricEventProduct,
@@ -45,6 +47,7 @@ class Metrics extends WebexPlugin {
45
47
  behavioralMetrics: BehavioralMetrics;
46
48
  operationalMetrics: OperationalMetrics;
47
49
  businessMetrics: BusinessMetrics;
50
+ preLoginMetrics: PreLoginMetrics;
48
51
  isReady = false;
49
52
 
50
53
  /**
@@ -87,6 +90,13 @@ class Metrics extends WebexPlugin {
87
90
  this.webex.once('ready', () => {
88
91
  // @ts-ignore
89
92
  this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
93
+ this.preLoginMetrics = new PreLoginMetrics(
94
+ // @ts-ignore
95
+ new PreLoginMetricsBatcher({}, {parent: this.webex}),
96
+ {},
97
+ // @ts-ignore
98
+ {parent: this.webex}
99
+ );
90
100
  this.isReady = true;
91
101
  this.setDelaySubmitClientEvents({
92
102
  shouldDelay: this.delaySubmitClientEvents,
@@ -251,6 +261,38 @@ class Metrics extends WebexPlugin {
251
261
  return this.businessMetrics.submitBusinessEvent({name, payload, table, metadata});
252
262
  }
253
263
 
264
+ /**
265
+ * Call Analyzer: Pre-Login Event
266
+ * @param args
267
+ */
268
+ submitPreLoginEvent({
269
+ name,
270
+ preLoginId,
271
+ payload,
272
+ metadata,
273
+ }: {
274
+ name: string;
275
+ preLoginId: string;
276
+ payload: EventPayload;
277
+ metadata?: EventPayload;
278
+ }): Promise<void> {
279
+ if (!this.isReady) {
280
+ // @ts-ignore
281
+ this.webex.logger.log(
282
+ `NewMetrics: @submitPreLoginEvent. Attempted to submit before webex.ready: ${name}`
283
+ );
284
+
285
+ return Promise.resolve();
286
+ }
287
+
288
+ return this.preLoginMetrics.submitPreLoginEvent({
289
+ name,
290
+ preLoginId,
291
+ payload,
292
+ metadata,
293
+ });
294
+ }
295
+
254
296
  /**
255
297
  * Call Analyzer: Media Quality Event
256
298
  * @param args
@@ -78,6 +78,8 @@ const PreLoginMetricsBatcher = Batcher.extend({
78
78
  `PreLoginMetricsBatcher: @submitHttpRequest#${batchId}. Request successful.`
79
79
  );
80
80
 
81
+ this.handleHttpResponseStatus(res?.statusCode, payload);
82
+
81
83
  return res;
82
84
  })
83
85
  .catch((err) => {
@@ -87,9 +89,33 @@ const PreLoginMetricsBatcher = Batcher.extend({
87
89
  `error: ${generateCommonErrorMetadata(err)}`
88
90
  );
89
91
 
92
+ this.handleHttpResponseStatus(err?.statusCode, payload);
93
+
90
94
  return Promise.reject(err);
91
95
  });
92
96
  },
97
+
98
+ /**
99
+ * React to the HTTP status code returned by the prelogin metrics endpoint.
100
+ * Only items submitted with `markTelemetryOptOutOnResponse: true` opt into
101
+ * this behavior.
102
+ * @param {number | undefined} statusCode
103
+ * @param {any[]} payload Items flushed in this HTTP batch.
104
+ * @returns {void}
105
+ */
106
+ handleHttpResponseStatus(statusCode: number | undefined, payload: any[]) {
107
+ const shouldMark =
108
+ Array.isArray(payload) &&
109
+ payload.some((item) => item?.markTelemetryOptOutOnResponse === true);
110
+
111
+ if (!shouldMark) {
112
+ return;
113
+ }
114
+
115
+ if (statusCode === 200) {
116
+ this.webex.internal.newMetrics?.callDiagnosticMetrics?.setIsTelemetryOptOutAutomatic(true);
117
+ }
118
+ },
93
119
  });
94
120
 
95
121
  export default PreLoginMetricsBatcher;
@@ -0,0 +1,94 @@
1
+ import GenericMetrics from './generic-metrics';
2
+ import {BusinessEvent, EventPayload} from './metrics.types';
3
+ import PreLoginMetricsBatcher from './prelogin-metrics-batcher';
4
+
5
+ /**
6
+ * @description Util class to handle PreLogin Metrics
7
+ * @export
8
+ * @class PreLoginMetrics
9
+ */
10
+ export default class PreLoginMetrics extends GenericMetrics {
11
+ private preLoginMetricsBatcher: typeof PreLoginMetricsBatcher;
12
+
13
+ /**
14
+ * Constructor
15
+ * @param {PreLoginMetricsBatcher} preLoginMetricsBatcher - Pre-login metrics batcher
16
+ * @param {any} attrs - Attributes
17
+ * @param {any} options - Options
18
+ * @constructor
19
+ */
20
+ constructor(
21
+ preLoginMetricsBatcher: typeof PreLoginMetricsBatcher,
22
+ attrs: any = {},
23
+ options: {parent?: any} = {}
24
+ ) {
25
+ super(attrs, options);
26
+ this.preLoginMetricsBatcher = preLoginMetricsBatcher;
27
+ }
28
+
29
+ /**
30
+ * Submit a business metric to our metrics endpoint.
31
+ * Routes to the correct table with the correct schema payload by table.
32
+ * @see https://confluence-eng-gpk2.cisco.com/conf/display/WAP/Business+metrics++-%3E+ROMA
33
+ * @param {Object} options - The options object
34
+ * @param {string} options.name - Name of the metric
35
+ * @param {string} options.preLoginId - ID to identify pre-login user
36
+ * @param {EventPayload} options.payload - User payload of the metric
37
+ * @param {EventPayload} [options.metadata] - Optional metadata to include outside of eventPayload.value
38
+ * @returns {Promise<void>} Promise that resolves when the metric is submitted
39
+ */
40
+ public submitPreLoginEvent({
41
+ name,
42
+ preLoginId,
43
+ payload,
44
+ metadata,
45
+ }: {
46
+ name: string;
47
+ preLoginId: string;
48
+ payload: EventPayload;
49
+ metadata?: EventPayload;
50
+ }): Promise<void> {
51
+ if (!metadata) {
52
+ metadata = {};
53
+ }
54
+ if (!metadata.appType) {
55
+ metadata.appType = 'Web Client';
56
+ }
57
+
58
+ const finalEvent = this.buildEvent(name, preLoginId, payload, metadata);
59
+
60
+ this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
61
+
62
+ return this.preLoginMetricsBatcher.request(finalEvent);
63
+ }
64
+
65
+ /**
66
+ * Builds a formatted event object for metrics submission.
67
+ * @param {string} metricName - Metric name
68
+ * @param {string} preLoginId - Pre-login user identifier
69
+ * @param {EventPayload} payload - Metric payload data
70
+ * @param {EventPayload} metadata - Additional metadata to include in the event
71
+ * @returns {object} Formatted metrics event object with type, eventPayload, and timestamp
72
+ */
73
+ private buildEvent(
74
+ metricName: string,
75
+ preLoginId: string,
76
+ payload: EventPayload,
77
+ metadata: EventPayload
78
+ ): BusinessEvent {
79
+ return {
80
+ type: ['business'],
81
+ eventPayload: {
82
+ metricName,
83
+ browserDetails: this.getBrowserDetails(),
84
+ context: this.getContext(),
85
+ timestamp: new Date().getTime(),
86
+ value: {
87
+ preLoginId,
88
+ ...metadata,
89
+ ...payload,
90
+ },
91
+ },
92
+ };
93
+ }
94
+ }
@@ -177,6 +177,49 @@ describe('plugin-metrics', () => {
177
177
  assert.lengthOf(webex.internal.metrics.batcher.queue, 0);
178
178
  });
179
179
  });
180
+
181
+ it('rejects the deferred without reenqueuing when batcherRetryOnNetworkError is false', () => {
182
+ webex.config.metrics = {...config.metrics, batcherRetryOnNetworkError: false};
183
+
184
+ webex.request = function () {
185
+ // noop
186
+ };
187
+
188
+ sinon.stub(webex, 'request').callsFake((options) => {
189
+ options.headers = {
190
+ trackingid: 'test-no-retry',
191
+ };
192
+
193
+ return Promise.reject(
194
+ new WebexHttpError.NetworkOrCORSError({
195
+ statusCode: 0,
196
+ options,
197
+ })
198
+ );
199
+ });
200
+
201
+ const promise = webex.internal.metrics.batcher.request({
202
+ key: 'testMetric',
203
+ });
204
+
205
+ return promiseTick(50)
206
+ .then(() => assert.lengthOf(webex.internal.metrics.batcher.queue, 1))
207
+ .then(() => clock.tick(config.metrics.batcherWait))
208
+ .then(() => assert.calledOnce(webex.request))
209
+ .then(() => promiseTick(50))
210
+ .then(() =>
211
+ promise.then(
212
+ () => {
213
+ assert.fail('promise should have been rejected');
214
+ },
215
+ (reason) => {
216
+ assert.instanceOf(reason, WebexHttpError.NetworkOrCORSError);
217
+ assert.lengthOf(webex.internal.metrics.batcher.queue, 0);
218
+ assert.calledOnce(webex.request);
219
+ }
220
+ )
221
+ );
222
+ });
180
223
  });
181
224
  });
182
225
  });
@@ -142,9 +142,7 @@ describe('plugin-metrics', () => {
142
142
  webex.internal.newMetrics.callDiagnosticLatencies.getDiffBetweenTimestamps = sinon
143
143
  .stub()
144
144
  .returns(10);
145
- webex.internal.newMetrics.callDiagnosticLatencies.getU2CTime = sinon
146
- .stub()
147
- .returns(20);
145
+ webex.internal.newMetrics.callDiagnosticLatencies.getU2CTime = sinon.stub().returns(20);
148
146
  webex.internal.newMetrics.callDiagnosticLatencies.getReachabilityClustersReqResp = sinon
149
147
  .stub()
150
148
  .returns(10);
@@ -165,7 +163,7 @@ describe('plugin-metrics', () => {
165
163
  registerWDMDeviceJMT: 10,
166
164
  showInterstitialTime: 10,
167
165
  getU2CTime: 20,
168
- getReachabilityClustersReqResp: 10
166
+ getReachabilityClustersReqResp: 10,
169
167
  },
170
168
  });
171
169
  assert.lengthOf(
@@ -189,12 +187,17 @@ describe('plugin-metrics', () => {
189
187
  webex.internal.newMetrics.callDiagnosticLatencies.getDownloadTimeJMT = sinon
190
188
  .stub()
191
189
  .returns(100);
192
- webex.internal.newMetrics.callDiagnosticLatencies.getClickToInterstitialWithUserDelay = sinon
193
- .stub()
194
- .returns(43);
190
+ webex.internal.newMetrics.callDiagnosticLatencies.getClickToInterstitialWithUserDelay =
191
+ sinon.stub().returns(43);
195
192
  webex.internal.newMetrics.callDiagnosticLatencies.getTotalJMTWithUserDelay = sinon
196
193
  .stub()
197
194
  .returns(64);
195
+ webex.internal.newMetrics.callDiagnosticLatencies.getInterstitialToJoinOK = sinon
196
+ .stub()
197
+ .returns(10);
198
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalJMT = sinon
199
+ .stub()
200
+ .returns(20);
198
201
  const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
199
202
  //@ts-ignore
200
203
  {event: {name: 'client.locus.join.response'}}
@@ -346,9 +349,15 @@ describe('plugin-metrics', () => {
346
349
  webex.internal.newMetrics.callDiagnosticLatencies.getInterstitialToJoinOK = sinon
347
350
  .stub()
348
351
  .returns(7);
349
- webex.internal.newMetrics.callDiagnosticLatencies.getStayLobbyTime = sinon
352
+ webex.internal.newMetrics.callDiagnosticLatencies.getStayLobbyTime = sinon
353
+ .stub()
354
+ .returns(1);
355
+ webex.internal.newMetrics.callDiagnosticLatencies.getStayLobbyTimeCappedBy = sinon
350
356
  .stub()
351
357
  .returns(1);
358
+ webex.internal.newMetrics.callDiagnosticLatencies.getTotalMediaJMT = sinon
359
+ .stub()
360
+ .returns(44);
352
361
  webex.internal.newMetrics.callDiagnosticLatencies.getTotalMediaJMTWithUserDelay = sinon
353
362
  .stub()
354
363
  .returns(43);
@@ -369,10 +378,9 @@ describe('plugin-metrics', () => {
369
378
  assert.deepEqual(webex.request.getCalls()[0].args[0].body.metrics[0].eventPayload.event, {
370
379
  name: 'client.media-engine.ready',
371
380
  joinTimes: {
372
- totalMediaJMT: 61,
381
+ totalMediaJMT: 44,
373
382
  interstitialToMediaOKJMT: 22,
374
- callInitMediaEngineReady: 10,
375
- stayLobbyTime: 1,
383
+ callInitMediaEngineReady: 22,
376
384
  totalMediaJMTWithUserDelay: 43,
377
385
  totalJMTWithUserDelay: 64,
378
386
  },
@@ -382,6 +390,34 @@ describe('plugin-metrics', () => {
382
390
  0
383
391
  );
384
392
  });
393
+
394
+ it('appends the correct join times to the request for client.lobby.exited', async () => {
395
+ webex.internal.newMetrics.callDiagnosticLatencies.getStayLobbyTime = sinon
396
+ .stub()
397
+ .returns(10);
398
+
399
+ const promise = webex.internal.newMetrics.callDiagnosticMetrics.submitToCallDiagnostics(
400
+ //@ts-ignore
401
+ {event: {name: 'client.lobby.exited'}}
402
+ );
403
+ await flushPromises();
404
+ clock.tick(config.metrics.batcherWait);
405
+
406
+ await promise;
407
+
408
+ //@ts-ignore
409
+ assert.calledOnce(webex.request);
410
+ assert.deepEqual(webex.request.getCalls()[0].args[0].body.metrics[0].eventPayload.event, {
411
+ name: 'client.lobby.exited',
412
+ joinTimes: {
413
+ stayLobbyTime: 10,
414
+ },
415
+ });
416
+ assert.lengthOf(
417
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.queue,
418
+ 0
419
+ );
420
+ });
385
421
  });
386
422
 
387
423
  describe('when the request fails', () => {
@@ -480,5 +516,141 @@ describe('plugin-metrics', () => {
480
516
  assert.deepEqual(prepareDiagnosticMetricItemCalls[0].args[1].type, ['diagnostic-event']);
481
517
  });
482
518
  });
519
+
520
+ describe('#submitHttpRequest', () => {
521
+ it('calls handleHttpResponseStatus with response status on success', async () => {
522
+ const payload = [
523
+ {
524
+ eventPayload: {event: 'my.event'},
525
+ type: ['diagnostic-event'],
526
+ },
527
+ ];
528
+
529
+ webex.request = sinon.stub().resolves({statusCode: 200});
530
+
531
+ const handleHttpResponseStatusSpy = sinon.spy(
532
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher,
533
+ 'handleHttpResponseStatus'
534
+ );
535
+
536
+ const promise =
537
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.submitHttpRequest(
538
+ payload
539
+ );
540
+
541
+ assert.deepEqual(handleHttpResponseStatusSpy.getCalls().length, 0);
542
+
543
+ await flushPromises();
544
+ clock.tick(config.metrics.batcherWait);
545
+
546
+ await promise;
547
+
548
+ assert.calledOnce(webex.request);
549
+ assert.deepEqual(handleHttpResponseStatusSpy.getCalls().length, 1);
550
+ assert.deepEqual(handleHttpResponseStatusSpy.args[0][0], 200);
551
+ assert.deepEqual(handleHttpResponseStatusSpy.args[0][1], payload);
552
+ });
553
+
554
+ it('calls handleHttpResponseStatus with error status on failure', async () => {
555
+ const payload = [
556
+ {
557
+ eventPayload: {event: 'my.event'},
558
+ type: ['diagnostic-event'],
559
+ },
560
+ ];
561
+
562
+ webex.request = sinon.stub().rejects({statusCode: 503});
563
+
564
+ const handleHttpResponseStatusSpy = sinon.spy(
565
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher,
566
+ 'handleHttpResponseStatus'
567
+ );
568
+
569
+ const promise =
570
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.submitHttpRequest(
571
+ payload
572
+ );
573
+
574
+ assert.deepEqual(handleHttpResponseStatusSpy.getCalls().length, 0);
575
+
576
+ await flushPromises();
577
+ clock.tick(config.metrics.batcherWait);
578
+
579
+ let error: any;
580
+
581
+ try {
582
+ await promise;
583
+ } catch (err) {
584
+ error = err;
585
+ }
586
+
587
+ assert.deepEqual(error.statusCode, 503);
588
+ assert.calledOnce(webex.request);
589
+ assert.deepEqual(handleHttpResponseStatusSpy.getCalls().length, 1);
590
+ assert.deepEqual(handleHttpResponseStatusSpy.args[0][0], 503);
591
+ assert.deepEqual(handleHttpResponseStatusSpy.args[0][1], payload);
592
+ });
593
+ });
594
+
595
+ describe('#handleHttpResponseStatus', () => {
596
+ let setIsTelemetryOptOutAutomaticStub;
597
+
598
+ beforeEach(() => {
599
+ setIsTelemetryOptOutAutomaticStub = sinon.stub(
600
+ webex.internal.newMetrics.callDiagnosticMetrics,
601
+ 'setIsTelemetryOptOutAutomatic'
602
+ );
603
+ });
604
+
605
+ [201, 400, 503, undefined].forEach((statusCode) => {
606
+ it(`does not call setIsTelemetryOptOutAutomatic() when statusCode is ${statusCode} and markTelemetryOptOutOnResponse is true`, () => {
607
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.handleHttpResponseStatus(
608
+ statusCode,
609
+ [{markTelemetryOptOutOnResponse: true}]
610
+ );
611
+
612
+ assert.notCalled(setIsTelemetryOptOutAutomaticStub);
613
+ });
614
+ });
615
+
616
+ it('calls setIsTelemetryOptOutAutomatic(true) when statusCode is 200 and markTelemetryOptOutOnResponse is true', () => {
617
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.handleHttpResponseStatus(
618
+ 200,
619
+ [{markTelemetryOptOutOnResponse: true}]
620
+ );
621
+
622
+ assert.calledOnce(setIsTelemetryOptOutAutomaticStub);
623
+ assert.calledWithExactly(setIsTelemetryOptOutAutomaticStub, true);
624
+ });
625
+
626
+ [200, 201, 400, 503, undefined].forEach((statusCode) => {
627
+ it(`does not call setIsTelemetryOptOutAutomatic when shouldMark is false (statusCode: ${statusCode})`, () => {
628
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.handleHttpResponseStatus(
629
+ statusCode,
630
+ [{markTelemetryOptOutOnResponse: false}]
631
+ );
632
+
633
+ assert.notCalled(setIsTelemetryOptOutAutomaticStub);
634
+ });
635
+ });
636
+
637
+ it('does not call setIsTelemetryOptOutAutomatic when payload is empty', () => {
638
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.handleHttpResponseStatus(
639
+ 200,
640
+ []
641
+ );
642
+
643
+ assert.notCalled(setIsTelemetryOptOutAutomaticStub);
644
+ });
645
+
646
+ it('does not call setIsTelemetryOptOutAutomatic when payload is not an array', () => {
647
+ webex.internal.newMetrics.callDiagnosticMetrics.callDiagnosticEventsBatcher.handleHttpResponseStatus(
648
+ 200,
649
+ null
650
+ );
651
+
652
+ assert.notCalled(setIsTelemetryOptOutAutomaticStub);
653
+ });
654
+ });
483
655
  });
484
656
  });