@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.
- package/dist/batcher.js +3 -0
- package/dist/batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +23 -0
- package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +68 -50
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +60 -8
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +44 -4
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +17 -3
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/generic-metrics.js +8 -6
- package/dist/generic-metrics.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.types.js.map +1 -1
- package/dist/new-metrics.js +56 -20
- package/dist/new-metrics.js.map +1 -1
- package/dist/prelogin-metrics-batcher.js +23 -0
- package/dist/prelogin-metrics-batcher.js.map +1 -1
- package/dist/prelogin-metrics.js +106 -0
- package/dist/prelogin-metrics.js.map +1 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +9 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +57 -20
- package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +23 -2
- package/dist/types/call-diagnostic/config.d.ts +6 -19
- package/dist/types/config.d.ts +16 -15
- package/dist/types/index.d.ts +2 -1
- package/dist/types/metrics.types.d.ts +4 -4
- package/dist/types/new-metrics.d.ts +12 -0
- package/dist/types/prelogin-metrics.d.ts +47 -0
- package/package.json +11 -11
- package/src/batcher.js +4 -0
- package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +26 -0
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +139 -70
- package/src/call-diagnostic/call-diagnostic-metrics.ts +52 -1
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +49 -2
- package/src/call-diagnostic/config.ts +15 -0
- package/src/config.js +1 -0
- package/src/generic-metrics.ts +6 -6
- package/src/index.ts +2 -0
- package/src/metrics.types.ts +3 -3
- package/src/new-metrics.ts +42 -0
- package/src/prelogin-metrics-batcher.ts +26 -0
- package/src/prelogin-metrics.ts +94 -0
- package/test/unit/spec/batcher.js +43 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +183 -11
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +324 -366
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +724 -159
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +92 -3
- package/test/unit/spec/prelogin-metrics-batcher.ts +190 -36
- 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
package/src/generic-metrics.ts
CHANGED
|
@@ -79,7 +79,7 @@ export default abstract class GenericMetrics extends StatelessWebexPlugin {
|
|
|
79
79
|
device: {
|
|
80
80
|
id: this.getDeviceId(),
|
|
81
81
|
},
|
|
82
|
-
locale: window
|
|
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
|
|
97
|
+
browserHeight: window?.innerHeight,
|
|
98
98
|
browserVersion: getBrowserVersion(),
|
|
99
|
-
browserWidth: window
|
|
100
|
-
domain: window
|
|
101
|
-
inIframe: window
|
|
102
|
-
locale: window
|
|
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,
|
package/src/metrics.types.ts
CHANGED
|
@@ -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:
|
|
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'
|
package/src/new-metrics.ts
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
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:
|
|
381
|
+
totalMediaJMT: 44,
|
|
373
382
|
interstitialToMediaOKJMT: 22,
|
|
374
|
-
callInitMediaEngineReady:
|
|
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
|
});
|