@webex/internal-plugin-metrics 3.11.0 → 3.12.0-llmrefactor.1
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/automated-user.js +19 -0
- package/dist/automated-user.js.map +1 -0
- package/dist/batcher.js +3 -0
- package/dist/batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +639 -65
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +168 -76
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +18 -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 +22 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.types.js +4 -0
- package/dist/metrics.types.js.map +1 -1
- package/dist/new-metrics.js +69 -23
- package/dist/new-metrics.js.map +1 -1
- package/dist/prelogin-metrics.js +106 -0
- package/dist/prelogin-metrics.js.map +1 -0
- package/dist/types/automated-user.d.ts +2 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +79 -15
- package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +5 -0
- package/dist/types/call-diagnostic/config.d.ts +5 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/index.d.ts +6 -3
- package/dist/types/metrics.types.d.ts +6 -4
- package/dist/types/new-metrics.d.ts +16 -0
- package/dist/types/prelogin-metrics.d.ts +47 -0
- package/package.json +12 -11
- package/src/automated-user.ts +16 -0
- package/src/batcher.js +4 -0
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +800 -75
- package/src/call-diagnostic/call-diagnostic-metrics.ts +102 -15
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +22 -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 +7 -0
- package/src/metrics.types.ts +18 -5
- package/src/new-metrics.ts +54 -4
- package/src/prelogin-metrics.ts +94 -0
- package/test/unit/spec/automated-user.ts +43 -0
- package/test/unit/spec/batcher.js +43 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +47 -11
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1550 -416
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +887 -159
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +61 -3
- package/test/unit/spec/new-metrics.ts +39 -2
- package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
- package/test/unit/spec/prelogin-metrics.ts +132 -0
|
@@ -6,6 +6,7 @@ import uuid from 'uuid';
|
|
|
6
6
|
import {merge} from 'lodash';
|
|
7
7
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
8
8
|
import {getOSNameInternal} from '../metrics';
|
|
9
|
+
import * as AutomatedUserUtils from '../automated-user';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
anonymizeIPAddress,
|
|
@@ -99,7 +100,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
99
100
|
// @ts-ignore
|
|
100
101
|
private preLoginMetricsBatcher: PreLoginMetricsBatcher;
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
// lazy getter to avoid @ts-ignore on every call site
|
|
104
|
+
private get logger(): any {
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
return this.webex.logger;
|
|
107
|
+
}
|
|
108
|
+
|
|
103
109
|
private hasLoggedBrowserSerial: boolean;
|
|
104
110
|
private device: any;
|
|
105
111
|
private delayedClientEvents: DelayedClientEvent[] = [];
|
|
@@ -108,6 +114,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
108
114
|
private isMercuryConnected = false;
|
|
109
115
|
private eventLimitTracker: Map<string, number> = new Map();
|
|
110
116
|
private eventLimitWarningsLogged: Set<string> = new Set();
|
|
117
|
+
private isTelemetryOptOutManual = false;
|
|
118
|
+
private isTelemetryOptOutAutomatic = false;
|
|
111
119
|
|
|
112
120
|
// the default validator before piping an event to the batcher
|
|
113
121
|
// this function can be overridden by the user
|
|
@@ -124,8 +132,6 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
124
132
|
constructor(...args) {
|
|
125
133
|
super(...args);
|
|
126
134
|
// @ts-ignore
|
|
127
|
-
this.logger = this.webex.logger;
|
|
128
|
-
// @ts-ignore
|
|
129
135
|
this.callDiagnosticEventsBatcher = new CallDiagnosticEventsBatcher({}, {parent: this.webex});
|
|
130
136
|
// @ts-ignore
|
|
131
137
|
this.preLoginMetricsBatcher = new PreLoginMetricsBatcher({}, {parent: this.webex});
|
|
@@ -145,6 +151,58 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
145
151
|
return null;
|
|
146
152
|
}
|
|
147
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Returns the user activation state reported from the browser's navigator.userActivation API
|
|
156
|
+
* @returns object with hasBeenActive and isActive booleans, or undefined if unavailable
|
|
157
|
+
*/
|
|
158
|
+
getUserActivation(): {hasBeenActive: boolean; isActive: boolean} | undefined {
|
|
159
|
+
const userActivation =
|
|
160
|
+
typeof navigator !== 'undefined'
|
|
161
|
+
? (navigator as {userActivation?: {hasBeenActive: boolean; isActive: boolean}})
|
|
162
|
+
.userActivation
|
|
163
|
+
: undefined;
|
|
164
|
+
|
|
165
|
+
if (userActivation) {
|
|
166
|
+
return {
|
|
167
|
+
hasBeenActive: userActivation.hasBeenActive,
|
|
168
|
+
isActive: userActivation.isActive,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Returns the telemetryOptOut value of the current user
|
|
177
|
+
* @returns one of 'manual', 'automatic', undefined
|
|
178
|
+
*/
|
|
179
|
+
public getTelemetryOptOut() {
|
|
180
|
+
if (this.isTelemetryOptOutManual) {
|
|
181
|
+
return 'manual';
|
|
182
|
+
}
|
|
183
|
+
if (this.isTelemetryOptOutAutomatic) {
|
|
184
|
+
return 'automatic';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Sets the manual telemetry opt-out status for the current user
|
|
192
|
+
* @param value - boolean value indicating manual telemetry opt-out status
|
|
193
|
+
*/
|
|
194
|
+
public setIsTelemetryOptOutManual(value: boolean) {
|
|
195
|
+
this.isTelemetryOptOutManual = value;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Sets the automatic telemetry opt-out status for the current user
|
|
200
|
+
* @param value - boolean value indicating automatic telemetry opt-out status
|
|
201
|
+
*/
|
|
202
|
+
public setIsTelemetryOptOutAutomatic(value: boolean) {
|
|
203
|
+
this.isTelemetryOptOutAutomatic = value;
|
|
204
|
+
}
|
|
205
|
+
|
|
148
206
|
/**
|
|
149
207
|
* Returns if the meeting has converged architecture enabled
|
|
150
208
|
* @param options.meetingId
|
|
@@ -190,6 +248,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
190
248
|
|
|
191
249
|
// if ConvergedArchitecture enable and isConvergedWebinarWebcast -- then webcast
|
|
192
250
|
if (meetingInfo?.enableConvergedArchitecture && meetingInfo?.enableEvent) {
|
|
251
|
+
// if enableConvergedWebinarLargeScale - then large scale webinar
|
|
252
|
+
if (meetingInfo?.enableConvergedWebinarLargeScale) {
|
|
253
|
+
return WEBEX_SUB_SERVICE_TYPES.LARGE_SCALE_WEBINAR;
|
|
254
|
+
}
|
|
255
|
+
|
|
193
256
|
return meetingInfo?.isConvergedWebinarWebcast
|
|
194
257
|
? WEBEX_SUB_SERVICE_TYPES.WEBCAST
|
|
195
258
|
: WEBEX_SUB_SERVICE_TYPES.WEBINAR;
|
|
@@ -213,12 +276,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
213
276
|
getOrigin(options: GetOriginOptions, meetingId?: string) {
|
|
214
277
|
const defaultClientType: ClientType =
|
|
215
278
|
// @ts-ignore
|
|
216
|
-
this.webex.meetings
|
|
279
|
+
this.webex.meetings?.config?.metrics?.clientType;
|
|
217
280
|
const defaultSubClientType: SubClientType =
|
|
218
281
|
// @ts-ignore
|
|
219
|
-
this.webex.meetings
|
|
282
|
+
this.webex.meetings?.config?.metrics?.subClientType;
|
|
220
283
|
// @ts-ignore
|
|
221
|
-
const providedClientVersion: string = this.webex.meetings
|
|
284
|
+
const providedClientVersion: string = this.webex.meetings?.config?.metrics?.clientVersion;
|
|
222
285
|
// @ts-ignore
|
|
223
286
|
const defaultSDKClientVersion = `${CLIENT_NAME}/${this.webex.version}`;
|
|
224
287
|
|
|
@@ -229,6 +292,18 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
229
292
|
versionMetadata = extractVersionMetadata(providedClientVersion);
|
|
230
293
|
}
|
|
231
294
|
|
|
295
|
+
// Browser details and the support verdict can all be supplied by the host client, which is able
|
|
296
|
+
// to resolve wrapper browsers (Electron, WebOS) to the engine that actually determines
|
|
297
|
+
// capability. The browser fields fall back to the SDK's own detection when not supplied, the
|
|
298
|
+
// same way clientVersion falls back to the SDK version.
|
|
299
|
+
const {
|
|
300
|
+
browser: providedBrowser,
|
|
301
|
+
browserVersion: providedBrowserVersion,
|
|
302
|
+
isSupportedBrowserFamily,
|
|
303
|
+
isOutdatedBrowserVersion,
|
|
304
|
+
// @ts-ignore
|
|
305
|
+
} = this.webex.meetings?.config?.metrics ?? {};
|
|
306
|
+
|
|
232
307
|
if (!this.hasLoggedBrowserSerial) {
|
|
233
308
|
this.logger.log(
|
|
234
309
|
CALL_DIAGNOSTIC_LOG_IDENTIFIER,
|
|
@@ -258,25 +333,27 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
258
333
|
...versionMetadata,
|
|
259
334
|
publicNetworkPrefix:
|
|
260
335
|
// @ts-ignore
|
|
261
|
-
anonymizeIPAddress(this.webex.meetings
|
|
336
|
+
anonymizeIPAddress(this.webex.meetings?.geoHintInfo?.clientAddress) || undefined,
|
|
262
337
|
localNetworkPrefix:
|
|
263
338
|
anonymizeIPAddress(
|
|
264
339
|
// @ts-ignore
|
|
265
|
-
this.webex.meetings
|
|
266
|
-
|
|
340
|
+
this.webex.meetings?.meetingCollection
|
|
341
|
+
?.get(meetingId)
|
|
267
342
|
?.statsAnalyzer?.getLocalIpAddress()
|
|
268
343
|
) || undefined,
|
|
269
344
|
osVersion: getOSVersion() || 'unknown',
|
|
270
345
|
subClientType: options?.subClientType || defaultSubClientType,
|
|
271
346
|
os: getOSNameInternal(),
|
|
272
|
-
browser: getBrowserName(),
|
|
273
|
-
browserVersion: getBrowserVersion(),
|
|
347
|
+
browser: providedBrowser || getBrowserName(),
|
|
348
|
+
browserVersion: providedBrowserVersion || getBrowserVersion(),
|
|
349
|
+
...(isSupportedBrowserFamily === undefined ? {} : {isSupportedBrowserFamily}),
|
|
350
|
+
...(isOutdatedBrowserVersion === undefined ? {} : {isOutdatedBrowserVersion}),
|
|
274
351
|
},
|
|
275
352
|
};
|
|
276
353
|
|
|
277
354
|
if (meetingId) {
|
|
278
355
|
// @ts-ignore
|
|
279
|
-
const meeting = this.webex.meetings
|
|
356
|
+
const meeting = this.webex.meetings?.getBasicMeetingInformation(meetingId);
|
|
280
357
|
if (meeting?.environment) {
|
|
281
358
|
origin.environment = meeting.environment;
|
|
282
359
|
}
|
|
@@ -366,6 +443,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
366
443
|
if (meeting?.locusInfo?.fullState) {
|
|
367
444
|
identifiers.locusUrl = meeting.locusUrl;
|
|
368
445
|
identifiers.locusId = meeting.locusUrl && meeting.locusUrl.split('/').pop();
|
|
446
|
+
identifiers.locusSessionId = meeting.locusInfo.fullState.sessionId;
|
|
369
447
|
identifiers.locusStartTime =
|
|
370
448
|
meeting.locusInfo.fullState && meeting.locusInfo.fullState.lastActive;
|
|
371
449
|
}
|
|
@@ -425,7 +503,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
425
503
|
sent: 'not_defined_yet',
|
|
426
504
|
},
|
|
427
505
|
// @ts-ignore
|
|
428
|
-
senderCountryCode: this.webex.meetings
|
|
506
|
+
senderCountryCode: this.webex.meetings?.geoHintInfo?.countryCode,
|
|
429
507
|
event: eventData,
|
|
430
508
|
};
|
|
431
509
|
|
|
@@ -601,6 +679,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
601
679
|
mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
|
|
602
680
|
startTime: new Date().toISOString(),
|
|
603
681
|
},
|
|
682
|
+
webexSubServiceType: this.getSubServiceType(meeting),
|
|
604
683
|
};
|
|
605
684
|
|
|
606
685
|
// merge any new properties, or override existing ones
|
|
@@ -983,7 +1062,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
983
1062
|
sessionCorrelationId,
|
|
984
1063
|
});
|
|
985
1064
|
|
|
986
|
-
// create common event object
|
|
1065
|
+
// create common event object structure
|
|
987
1066
|
const commonEventObject = {
|
|
988
1067
|
name,
|
|
989
1068
|
canProceed: true,
|
|
@@ -996,6 +1075,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
996
1075
|
'loginType' in meeting.callStateForMetrics
|
|
997
1076
|
? meeting.callStateForMetrics.loginType
|
|
998
1077
|
: this.getCurLoginType(),
|
|
1078
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
999
1079
|
isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
|
|
1000
1080
|
meetingId,
|
|
1001
1081
|
}),
|
|
@@ -1004,6 +1084,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1004
1084
|
webexSubServiceType: this.getSubServiceType(meeting),
|
|
1005
1085
|
// @ts-ignore
|
|
1006
1086
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1087
|
+
isVipMeeting: meeting?.meetingInfo?.vipmeeting || false,
|
|
1088
|
+
isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
|
|
1089
|
+
userActivation: this.getUserActivation(),
|
|
1007
1090
|
};
|
|
1008
1091
|
|
|
1009
1092
|
const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
|
|
@@ -1123,8 +1206,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1123
1206
|
isMercuryConnected: this.isMercuryConnected,
|
|
1124
1207
|
},
|
|
1125
1208
|
loginType: this.getCurLoginType(),
|
|
1209
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1126
1210
|
// @ts-ignore
|
|
1127
1211
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1212
|
+
isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
|
|
1213
|
+
userActivation: this.getUserActivation(),
|
|
1128
1214
|
};
|
|
1129
1215
|
|
|
1130
1216
|
if (options.joinFlowVersion) {
|
|
@@ -1334,6 +1420,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1334
1420
|
eventPayload: event,
|
|
1335
1421
|
type: ['diagnostic-event'],
|
|
1336
1422
|
};
|
|
1423
|
+
|
|
1337
1424
|
this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
|
|
1338
1425
|
|
|
1339
1426
|
return this.preLoginMetricsBatcher.request(finalEvent);
|
|
@@ -1381,7 +1468,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1381
1468
|
},
|
|
1382
1469
|
headers: {},
|
|
1383
1470
|
// @ts-ignore
|
|
1384
|
-
waitForServiceTimeout: this.webex.internal.metrics
|
|
1471
|
+
waitForServiceTimeout: this.webex.internal.metrics?.config?.waitForServiceTimeout,
|
|
1385
1472
|
};
|
|
1386
1473
|
|
|
1387
1474
|
if (options.preLoginId) {
|
|
@@ -179,6 +179,16 @@ export const isSdpOfferCreationError = (rawError: any) => {
|
|
|
179
179
|
return false;
|
|
180
180
|
};
|
|
181
181
|
|
|
182
|
+
export const isWebrtcApiNotAvailableError = (
|
|
183
|
+
rawError: {code: number; message: string; name: string} | unknown
|
|
184
|
+
) => {
|
|
185
|
+
if ((rawError as {name: string}).name === ERROR_DESCRIPTIONS.WEBRTC_API_NOT_AVAILABLE) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return false;
|
|
190
|
+
};
|
|
191
|
+
|
|
182
192
|
/**
|
|
183
193
|
* Checks if the given error is a browser media error by its name.
|
|
184
194
|
* Returns true if the error name matches any known browser media error name in the mapping.
|
|
@@ -274,7 +284,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
274
284
|
devicePairingType: webex.devicemanager.getPairedMethod(),
|
|
275
285
|
deviceURL: pairedDevice.url,
|
|
276
286
|
isPersonalDevice: pairedDevice.mode === 'personal',
|
|
277
|
-
productName:
|
|
287
|
+
productName:
|
|
288
|
+
pairedDevice.devices?.length > 0 ? pairedDevice.devices[0]?.productName : undefined,
|
|
278
289
|
};
|
|
279
290
|
item.eventPayload.event.pairingState = 'paired';
|
|
280
291
|
item.eventPayload.event.pairedDevice = devicePayload;
|
|
@@ -351,7 +362,6 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
351
362
|
joinTimes.totalMediaJMT = cdl.getTotalMediaJMT();
|
|
352
363
|
joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
|
|
353
364
|
joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
|
|
354
|
-
joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
|
|
355
365
|
joinTimes.totalMediaJMTWithUserDelay = cdl.getTotalMediaJMTWithUserDelay();
|
|
356
366
|
joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
|
|
357
367
|
break;
|
|
@@ -359,6 +369,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
359
369
|
case 'client.media.tx.start':
|
|
360
370
|
audioSetupDelay.joinRespTxStart = cdl.getAudioJoinRespTxStart();
|
|
361
371
|
videoSetupDelay.joinRespTxStart = cdl.getVideoJoinRespTxStart();
|
|
372
|
+
break;
|
|
373
|
+
|
|
374
|
+
case 'client.lobby.exited':
|
|
375
|
+
joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
|
|
376
|
+
break;
|
|
362
377
|
}
|
|
363
378
|
|
|
364
379
|
if (!isEmpty(joinTimes)) {
|
|
@@ -375,6 +390,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
375
390
|
|
|
376
391
|
item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
|
|
377
392
|
|
|
393
|
+
// Mark call milestones in logs for easier filtering and analysis
|
|
394
|
+
if (eventName) {
|
|
395
|
+
webex.logger.log('Milestone,CallDiagnostic', eventName);
|
|
396
|
+
}
|
|
397
|
+
|
|
378
398
|
webex.logger.log(
|
|
379
399
|
`CallDiagnosticLatencies,prepareDiagnosticMetricItem: ${JSON.stringify({
|
|
380
400
|
latencies: Object.fromEntries(cdl.latencyTimestamps),
|
|
@@ -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
|
@@ -8,6 +8,7 @@ import Metrics from './metrics';
|
|
|
8
8
|
import config from './config';
|
|
9
9
|
import NewMetrics from './new-metrics';
|
|
10
10
|
import * as Utils from './utils';
|
|
11
|
+
import * as AutomatedUserUtils from './automated-user';
|
|
11
12
|
import {
|
|
12
13
|
ClientEvent,
|
|
13
14
|
ClientEventLeaveReason,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
SubmitMQE,
|
|
20
21
|
PreComputedLatencies,
|
|
21
22
|
SubmitFeatureEvent,
|
|
23
|
+
LocusSyncLatencyEventName,
|
|
22
24
|
} from './metrics.types';
|
|
23
25
|
import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
24
26
|
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
@@ -28,6 +30,7 @@ import BehavioralMetrics from './behavioral-metrics';
|
|
|
28
30
|
import OperationalMetrics from './operational-metrics';
|
|
29
31
|
import BusinessMetrics from './business-metrics';
|
|
30
32
|
import RtcMetrics from './rtcMetrics';
|
|
33
|
+
import PreLoginMetrics from './prelogin-metrics';
|
|
31
34
|
|
|
32
35
|
registerInternalPlugin('metrics', Metrics, {
|
|
33
36
|
config,
|
|
@@ -42,6 +45,7 @@ export {default, getOSNameInternal} from './metrics';
|
|
|
42
45
|
export {
|
|
43
46
|
config,
|
|
44
47
|
CALL_DIAGNOSTIC_CONFIG,
|
|
48
|
+
AutomatedUserUtils,
|
|
45
49
|
NewMetrics,
|
|
46
50
|
Utils,
|
|
47
51
|
CallDiagnosticUtils,
|
|
@@ -51,7 +55,9 @@ export {
|
|
|
51
55
|
OperationalMetrics,
|
|
52
56
|
BusinessMetrics,
|
|
53
57
|
RtcMetrics,
|
|
58
|
+
PreLoginMetrics,
|
|
54
59
|
};
|
|
60
|
+
export {isAutomatedUser, isAutomatedUserAgent} from './automated-user';
|
|
55
61
|
export type {
|
|
56
62
|
ClientEvent,
|
|
57
63
|
ClientEventLeaveReason,
|
|
@@ -63,4 +69,5 @@ export type {
|
|
|
63
69
|
SubmitBusinessEvent,
|
|
64
70
|
PreComputedLatencies,
|
|
65
71
|
SubmitFeatureEvent,
|
|
72
|
+
LocusSyncLatencyEventName,
|
|
66
73
|
};
|
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,
|
|
@@ -149,6 +149,17 @@ export type SubmitMQEOptions = {
|
|
|
149
149
|
globalMeetingId?: string;
|
|
150
150
|
};
|
|
151
151
|
|
|
152
|
+
export const LOCUS_SYNC_LATENCY_EVENT_NAMES = [
|
|
153
|
+
'internal.client.locus.sync.start',
|
|
154
|
+
'internal.client.locus.hashtree.request',
|
|
155
|
+
'internal.client.locus.hashtree.response',
|
|
156
|
+
'internal.client.locus.sync.request',
|
|
157
|
+
'internal.client.locus.sync.response',
|
|
158
|
+
'internal.client.locus.sync.message.received',
|
|
159
|
+
] as const;
|
|
160
|
+
|
|
161
|
+
export type LocusSyncLatencyEventName = (typeof LOCUS_SYNC_LATENCY_EVENT_NAMES)[number];
|
|
162
|
+
|
|
152
163
|
export type InternalEvent = {
|
|
153
164
|
name:
|
|
154
165
|
| 'internal.client.meetinginfo.request'
|
|
@@ -156,14 +167,14 @@ export type InternalEvent = {
|
|
|
156
167
|
| 'internal.register.device.request'
|
|
157
168
|
| 'internal.register.device.response'
|
|
158
169
|
| 'internal.reset.join.latencies'
|
|
159
|
-
| 'internal.client.meeting.click.joinbutton'
|
|
160
170
|
| 'internal.host.meeting.participant.admitted'
|
|
161
171
|
| 'internal.client.meeting.interstitial-window.showed'
|
|
162
172
|
| 'internal.client.interstitial-window.click.joinbutton'
|
|
163
173
|
| 'internal.client.add-media.turn-discovery.start'
|
|
164
174
|
| 'internal.client.add-media.turn-discovery.end'
|
|
165
175
|
| 'internal.client.share.initiated'
|
|
166
|
-
| 'internal.client.share.stopped'
|
|
176
|
+
| 'internal.client.share.stopped'
|
|
177
|
+
| LocusSyncLatencyEventName;
|
|
167
178
|
|
|
168
179
|
payload?: never;
|
|
169
180
|
options?: never;
|
|
@@ -197,7 +208,7 @@ export interface BusinessEventPayload {
|
|
|
197
208
|
metricName: string;
|
|
198
209
|
timestamp: number;
|
|
199
210
|
context: DeviceContext;
|
|
200
|
-
browserDetails:
|
|
211
|
+
browserDetails: object;
|
|
201
212
|
value: EventPayload;
|
|
202
213
|
}
|
|
203
214
|
|
|
@@ -319,12 +330,14 @@ export type PreComputedLatencies =
|
|
|
319
330
|
| 'internal.get.cluster.time'
|
|
320
331
|
| 'internal.click.to.interstitial'
|
|
321
332
|
| 'internal.click.to.interstitial.with.user.delay'
|
|
333
|
+
| 'internal.click.to.interstitial.for.client.jmt'
|
|
322
334
|
| 'internal.refresh.captcha.time'
|
|
323
335
|
| 'internal.exchange.ci.token.time'
|
|
324
336
|
| 'internal.get.u2c.time'
|
|
325
337
|
| 'internal.call.init.join.req'
|
|
326
338
|
| 'internal.other.app.api.time'
|
|
327
|
-
| 'internal.api.fetch.intelligence.models'
|
|
339
|
+
| 'internal.api.fetch.intelligence.models'
|
|
340
|
+
| 'internal.client.locus.sync.random.backoff';
|
|
328
341
|
|
|
329
342
|
export interface IdType {
|
|
330
343
|
meetingId?: string;
|
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,
|
|
@@ -28,6 +30,7 @@ import {
|
|
|
28
30
|
import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
|
|
29
31
|
import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
|
|
30
32
|
import {generateCommonErrorMetadata} from './utils';
|
|
33
|
+
import {isAutomatedUser as detectAutomatedUser} from './automated-user';
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* Metrics plugin to centralize all types of metrics.
|
|
@@ -45,6 +48,7 @@ class Metrics extends WebexPlugin {
|
|
|
45
48
|
behavioralMetrics: BehavioralMetrics;
|
|
46
49
|
operationalMetrics: OperationalMetrics;
|
|
47
50
|
businessMetrics: BusinessMetrics;
|
|
51
|
+
preLoginMetrics: PreLoginMetrics;
|
|
48
52
|
isReady = false;
|
|
49
53
|
|
|
50
54
|
/**
|
|
@@ -76,6 +80,8 @@ class Metrics extends WebexPlugin {
|
|
|
76
80
|
|
|
77
81
|
// @ts-ignore
|
|
78
82
|
this.callDiagnosticLatencies = new CallDiagnosticLatencies({}, {parent: this.webex});
|
|
83
|
+
// @ts-ignore
|
|
84
|
+
this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
|
|
79
85
|
this.onReady();
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -85,8 +91,13 @@ class Metrics extends WebexPlugin {
|
|
|
85
91
|
private onReady() {
|
|
86
92
|
// @ts-ignore
|
|
87
93
|
this.webex.once('ready', () => {
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
this.preLoginMetrics = new PreLoginMetrics(
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
new PreLoginMetricsBatcher({}, {parent: this.webex}),
|
|
97
|
+
{},
|
|
98
|
+
// @ts-ignore
|
|
99
|
+
{parent: this.webex}
|
|
100
|
+
);
|
|
90
101
|
this.isReady = true;
|
|
91
102
|
this.setDelaySubmitClientEvents({
|
|
92
103
|
shouldDelay: this.delaySubmitClientEvents,
|
|
@@ -172,6 +183,13 @@ class Metrics extends WebexPlugin {
|
|
|
172
183
|
return this.businessMetrics?.isReadyToSubmitEvents() ?? false;
|
|
173
184
|
}
|
|
174
185
|
|
|
186
|
+
/**
|
|
187
|
+
* @returns whether the current user agent belongs to an automated user
|
|
188
|
+
*/
|
|
189
|
+
isAutomatedUser() {
|
|
190
|
+
return detectAutomatedUser();
|
|
191
|
+
}
|
|
192
|
+
|
|
175
193
|
/**
|
|
176
194
|
* Behavioral event
|
|
177
195
|
* @param args
|
|
@@ -251,6 +269,38 @@ class Metrics extends WebexPlugin {
|
|
|
251
269
|
return this.businessMetrics.submitBusinessEvent({name, payload, table, metadata});
|
|
252
270
|
}
|
|
253
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Call Analyzer: Pre-Login Event
|
|
274
|
+
* @param args
|
|
275
|
+
*/
|
|
276
|
+
submitPreLoginEvent({
|
|
277
|
+
name,
|
|
278
|
+
preLoginId,
|
|
279
|
+
payload,
|
|
280
|
+
metadata,
|
|
281
|
+
}: {
|
|
282
|
+
name: string;
|
|
283
|
+
preLoginId: string;
|
|
284
|
+
payload: EventPayload;
|
|
285
|
+
metadata?: EventPayload;
|
|
286
|
+
}): Promise<void> {
|
|
287
|
+
if (!this.isReady) {
|
|
288
|
+
// @ts-ignore
|
|
289
|
+
this.webex.logger.log(
|
|
290
|
+
`NewMetrics: @submitPreLoginEvent. Attempted to submit before webex.ready: ${name}`
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
return Promise.resolve();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return this.preLoginMetrics.submitPreLoginEvent({
|
|
297
|
+
name,
|
|
298
|
+
preLoginId,
|
|
299
|
+
payload,
|
|
300
|
+
metadata,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
254
304
|
/**
|
|
255
305
|
* Call Analyzer: Media Quality Event
|
|
256
306
|
* @param args
|
|
@@ -283,7 +333,7 @@ class Metrics extends WebexPlugin {
|
|
|
283
333
|
payload?: RecursivePartial<FeatureEvent['payload']>;
|
|
284
334
|
options: any;
|
|
285
335
|
}) {
|
|
286
|
-
if (!this.
|
|
336
|
+
if (!this.isReady) {
|
|
287
337
|
// @ts-ignore
|
|
288
338
|
this.webex.logger.log(
|
|
289
339
|
`NewMetrics: @submitFeatureEvent. Attempted to submit before webex.ready. Event name: ${name}`
|
|
@@ -318,7 +368,7 @@ class Metrics extends WebexPlugin {
|
|
|
318
368
|
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
319
369
|
options?: SubmitClientEventOptions;
|
|
320
370
|
}): Promise<any> {
|
|
321
|
-
if (!this.
|
|
371
|
+
if (!this.isReady) {
|
|
322
372
|
// @ts-ignore
|
|
323
373
|
this.webex.logger.log(
|
|
324
374
|
`NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: ${name}`
|