@webex/internal-plugin-metrics 3.12.0-next.4 → 3.12.0-next.41
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 +638 -65
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +167 -76
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +16 -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/index.js +15 -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 +60 -17
- package/dist/new-metrics.js.map +1 -1
- package/dist/privacy-and-security-permission-enricher/constants.js +18 -0
- package/dist/privacy-and-security-permission-enricher/constants.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/index.js +143 -0
- package/dist/privacy-and-security-permission-enricher/index.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/types.js +7 -0
- package/dist/privacy-and-security-permission-enricher/types.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/utils.js +71 -0
- package/dist/privacy-and-security-permission-enricher/utils.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 +67 -14
- package/dist/types/call-diagnostic/config.d.ts +4 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/metrics.types.d.ts +7 -2
- package/dist/types/new-metrics.d.ts +17 -1
- package/dist/types/privacy-and-security-permission-enricher/constants.d.ts +6 -0
- package/dist/types/privacy-and-security-permission-enricher/index.d.ts +40 -0
- package/dist/types/privacy-and-security-permission-enricher/types.d.ts +14 -0
- package/dist/types/privacy-and-security-permission-enricher/utils.d.ts +5 -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 -73
- package/src/call-diagnostic/call-diagnostic-metrics.ts +101 -15
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
- package/src/call-diagnostic/config.ts +14 -0
- package/src/config.js +1 -0
- package/src/index.ts +7 -0
- package/src/metrics.types.ts +25 -3
- package/src/new-metrics.ts +53 -6
- package/src/privacy-and-security-permission-enricher/constants.ts +33 -0
- package/src/privacy-and-security-permission-enricher/index.ts +142 -0
- package/src/privacy-and-security-permission-enricher/types.ts +21 -0
- package/src/privacy-and-security-permission-enricher/utils.ts +82 -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 +14 -2
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1434 -303
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +852 -159
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
- package/test/unit/spec/new-metrics.ts +183 -50
- package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
- package/test/unit/spec/privacy-and-security-permission-enricher.ts +318 -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
|
}
|
|
@@ -426,7 +503,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
426
503
|
sent: 'not_defined_yet',
|
|
427
504
|
},
|
|
428
505
|
// @ts-ignore
|
|
429
|
-
senderCountryCode: this.webex.meetings
|
|
506
|
+
senderCountryCode: this.webex.meetings?.geoHintInfo?.countryCode,
|
|
430
507
|
event: eventData,
|
|
431
508
|
};
|
|
432
509
|
|
|
@@ -602,6 +679,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
602
679
|
mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
|
|
603
680
|
startTime: new Date().toISOString(),
|
|
604
681
|
},
|
|
682
|
+
webexSubServiceType: this.getSubServiceType(meeting),
|
|
605
683
|
};
|
|
606
684
|
|
|
607
685
|
// merge any new properties, or override existing ones
|
|
@@ -984,7 +1062,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
984
1062
|
sessionCorrelationId,
|
|
985
1063
|
});
|
|
986
1064
|
|
|
987
|
-
// create common event object
|
|
1065
|
+
// create common event object structure
|
|
988
1066
|
const commonEventObject = {
|
|
989
1067
|
name,
|
|
990
1068
|
canProceed: true,
|
|
@@ -997,6 +1075,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
997
1075
|
'loginType' in meeting.callStateForMetrics
|
|
998
1076
|
? meeting.callStateForMetrics.loginType
|
|
999
1077
|
: this.getCurLoginType(),
|
|
1078
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1000
1079
|
isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
|
|
1001
1080
|
meetingId,
|
|
1002
1081
|
}),
|
|
@@ -1005,6 +1084,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1005
1084
|
webexSubServiceType: this.getSubServiceType(meeting),
|
|
1006
1085
|
// @ts-ignore
|
|
1007
1086
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1087
|
+
isVipMeeting: meeting?.meetingInfo?.vipmeeting || false,
|
|
1088
|
+
isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
|
|
1089
|
+
userActivation: this.getUserActivation(),
|
|
1008
1090
|
};
|
|
1009
1091
|
|
|
1010
1092
|
const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
|
|
@@ -1124,8 +1206,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1124
1206
|
isMercuryConnected: this.isMercuryConnected,
|
|
1125
1207
|
},
|
|
1126
1208
|
loginType: this.getCurLoginType(),
|
|
1209
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1127
1210
|
// @ts-ignore
|
|
1128
1211
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1212
|
+
isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
|
|
1213
|
+
userActivation: this.getUserActivation(),
|
|
1129
1214
|
};
|
|
1130
1215
|
|
|
1131
1216
|
if (options.joinFlowVersion) {
|
|
@@ -1335,6 +1420,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1335
1420
|
eventPayload: event,
|
|
1336
1421
|
type: ['diagnostic-event'],
|
|
1337
1422
|
};
|
|
1423
|
+
|
|
1338
1424
|
this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
|
|
1339
1425
|
|
|
1340
1426
|
return this.preLoginMetricsBatcher.request(finalEvent);
|
|
@@ -1382,7 +1468,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1382
1468
|
},
|
|
1383
1469
|
headers: {},
|
|
1384
1470
|
// @ts-ignore
|
|
1385
|
-
waitForServiceTimeout: this.webex.internal.metrics
|
|
1471
|
+
waitForServiceTimeout: this.webex.internal.metrics?.config?.waitForServiceTimeout,
|
|
1386
1472
|
};
|
|
1387
1473
|
|
|
1388
1474
|
if (options.preLoginId) {
|
|
@@ -390,6 +390,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
390
390
|
|
|
391
391
|
item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
|
|
392
392
|
|
|
393
|
+
// Mark call milestones in logs for easier filtering and analysis
|
|
394
|
+
if (eventName) {
|
|
395
|
+
webex.logger.log('Milestone,CallDiagnostic', eventName);
|
|
396
|
+
}
|
|
397
|
+
|
|
393
398
|
webex.logger.log(
|
|
394
399
|
`CallDiagnosticLatencies,prepareDiagnosticMetricItem: ${JSON.stringify({
|
|
395
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
|
|
@@ -136,6 +137,7 @@ export const ERROR_DESCRIPTIONS = {
|
|
|
136
137
|
WEBRTC_API_NOT_AVAILABLE: 'WebrtcApiNotAvailableError',
|
|
137
138
|
WDM_RESTRICTED_REGION: 'WdmRestrictedRegion',
|
|
138
139
|
USER_NOT_ALLOWED_JOIN_WEBINAR: 'UserNotAllowedJoinWebinar',
|
|
140
|
+
INVALID_MEETING_INFO: 'InvalidMeetingInfo',
|
|
139
141
|
};
|
|
140
142
|
|
|
141
143
|
export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
@@ -146,6 +148,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
|
146
148
|
99002: 4100,
|
|
147
149
|
// Cannot find the data. Unkown meeting.
|
|
148
150
|
99009: 4100,
|
|
151
|
+
// The input parameters contain invalid item
|
|
152
|
+
99019: 4105,
|
|
149
153
|
// Meeting is not allow to cross env
|
|
150
154
|
58500: 4100,
|
|
151
155
|
// Input parameters contain invalit item
|
|
@@ -201,6 +205,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
|
201
205
|
423005: 4005,
|
|
202
206
|
// Wrong password or host key with too many requests
|
|
203
207
|
423006: 4005,
|
|
208
|
+
// PanelistPasswordError too many time,please input captcha code
|
|
209
|
+
423008: 4005,
|
|
204
210
|
// PasswordError with right captcha, please input captcha code
|
|
205
211
|
423010: 4005,
|
|
206
212
|
// PasswordOrHostKeyError with right captcha, please input captcha code
|
|
@@ -227,6 +233,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
|
227
233
|
403003: 4101,
|
|
228
234
|
// Attendee email is required
|
|
229
235
|
403030: 4101,
|
|
236
|
+
// webinar need login when un-invited attendee join
|
|
237
|
+
403106: 4104,
|
|
230
238
|
|
|
231
239
|
// ---- Locus ------
|
|
232
240
|
// FREE_USER_MAX_PARTICIPANTS_EXCEEDED
|
|
@@ -696,6 +704,12 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
|
|
|
696
704
|
category: 'expected',
|
|
697
705
|
fatal: true,
|
|
698
706
|
},
|
|
707
|
+
4105: {
|
|
708
|
+
errorDescription: ERROR_DESCRIPTIONS.INVALID_MEETING_INFO,
|
|
709
|
+
category: 'expected',
|
|
710
|
+
fatal: false,
|
|
711
|
+
shownToUser: true,
|
|
712
|
+
},
|
|
699
713
|
2729: {
|
|
700
714
|
errorDescription: ERROR_DESCRIPTIONS.NO_MEDIA_FOUND,
|
|
701
715
|
category: 'expected',
|
package/src/config.js
CHANGED
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,8 @@ import {
|
|
|
19
20
|
SubmitMQE,
|
|
20
21
|
PreComputedLatencies,
|
|
21
22
|
SubmitFeatureEvent,
|
|
23
|
+
LocusSyncLatencyEventName,
|
|
24
|
+
PrivacyAndSecurityPermission,
|
|
22
25
|
} from './metrics.types';
|
|
23
26
|
import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
24
27
|
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
@@ -43,6 +46,7 @@ export {default, getOSNameInternal} from './metrics';
|
|
|
43
46
|
export {
|
|
44
47
|
config,
|
|
45
48
|
CALL_DIAGNOSTIC_CONFIG,
|
|
49
|
+
AutomatedUserUtils,
|
|
46
50
|
NewMetrics,
|
|
47
51
|
Utils,
|
|
48
52
|
CallDiagnosticUtils,
|
|
@@ -54,6 +58,7 @@ export {
|
|
|
54
58
|
RtcMetrics,
|
|
55
59
|
PreLoginMetrics,
|
|
56
60
|
};
|
|
61
|
+
export {isAutomatedUser, isAutomatedUserAgent} from './automated-user';
|
|
57
62
|
export type {
|
|
58
63
|
ClientEvent,
|
|
59
64
|
ClientEventLeaveReason,
|
|
@@ -65,4 +70,6 @@ export type {
|
|
|
65
70
|
SubmitBusinessEvent,
|
|
66
71
|
PreComputedLatencies,
|
|
67
72
|
SubmitFeatureEvent,
|
|
73
|
+
LocusSyncLatencyEventName,
|
|
74
|
+
PrivacyAndSecurityPermission,
|
|
68
75
|
};
|
package/src/metrics.types.ts
CHANGED
|
@@ -23,6 +23,15 @@ export type ClientUserNameInput = NonNullable<RawClientEvent['userNameInput']>;
|
|
|
23
23
|
|
|
24
24
|
export type ClientEmailInput = NonNullable<RawClientEvent['emailInput']>;
|
|
25
25
|
|
|
26
|
+
export type PrivacyAndSecurityPermission = NonNullable<
|
|
27
|
+
RawClientEvent['privacyAndSecurityPermission']
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
export type PrivacyAndSecurityPermissionResource = keyof PrivacyAndSecurityPermission;
|
|
31
|
+
|
|
32
|
+
export type PrivacyAndSecurityPermissionState =
|
|
33
|
+
PrivacyAndSecurityPermission[PrivacyAndSecurityPermissionResource];
|
|
34
|
+
|
|
26
35
|
export type BrowserLaunchMethodType = NonNullable<
|
|
27
36
|
RawEvent['origin']['clientInfo']
|
|
28
37
|
>['browserLaunchMethod'];
|
|
@@ -149,6 +158,17 @@ export type SubmitMQEOptions = {
|
|
|
149
158
|
globalMeetingId?: string;
|
|
150
159
|
};
|
|
151
160
|
|
|
161
|
+
export const LOCUS_SYNC_LATENCY_EVENT_NAMES = [
|
|
162
|
+
'internal.client.locus.sync.start',
|
|
163
|
+
'internal.client.locus.hashtree.request',
|
|
164
|
+
'internal.client.locus.hashtree.response',
|
|
165
|
+
'internal.client.locus.sync.request',
|
|
166
|
+
'internal.client.locus.sync.response',
|
|
167
|
+
'internal.client.locus.sync.message.received',
|
|
168
|
+
] as const;
|
|
169
|
+
|
|
170
|
+
export type LocusSyncLatencyEventName = (typeof LOCUS_SYNC_LATENCY_EVENT_NAMES)[number];
|
|
171
|
+
|
|
152
172
|
export type InternalEvent = {
|
|
153
173
|
name:
|
|
154
174
|
| 'internal.client.meetinginfo.request'
|
|
@@ -156,14 +176,14 @@ export type InternalEvent = {
|
|
|
156
176
|
| 'internal.register.device.request'
|
|
157
177
|
| 'internal.register.device.response'
|
|
158
178
|
| 'internal.reset.join.latencies'
|
|
159
|
-
| 'internal.client.meeting.click.joinbutton'
|
|
160
179
|
| 'internal.host.meeting.participant.admitted'
|
|
161
180
|
| 'internal.client.meeting.interstitial-window.showed'
|
|
162
181
|
| 'internal.client.interstitial-window.click.joinbutton'
|
|
163
182
|
| 'internal.client.add-media.turn-discovery.start'
|
|
164
183
|
| 'internal.client.add-media.turn-discovery.end'
|
|
165
184
|
| 'internal.client.share.initiated'
|
|
166
|
-
| 'internal.client.share.stopped'
|
|
185
|
+
| 'internal.client.share.stopped'
|
|
186
|
+
| LocusSyncLatencyEventName;
|
|
167
187
|
|
|
168
188
|
payload?: never;
|
|
169
189
|
options?: never;
|
|
@@ -319,12 +339,14 @@ export type PreComputedLatencies =
|
|
|
319
339
|
| 'internal.get.cluster.time'
|
|
320
340
|
| 'internal.click.to.interstitial'
|
|
321
341
|
| 'internal.click.to.interstitial.with.user.delay'
|
|
342
|
+
| 'internal.click.to.interstitial.for.client.jmt'
|
|
322
343
|
| 'internal.refresh.captcha.time'
|
|
323
344
|
| 'internal.exchange.ci.token.time'
|
|
324
345
|
| 'internal.get.u2c.time'
|
|
325
346
|
| 'internal.call.init.join.req'
|
|
326
347
|
| 'internal.other.app.api.time'
|
|
327
|
-
| 'internal.api.fetch.intelligence.models'
|
|
348
|
+
| 'internal.api.fetch.intelligence.models'
|
|
349
|
+
| 'internal.client.locus.sync.random.backoff';
|
|
328
350
|
|
|
329
351
|
export interface IdType {
|
|
330
352
|
meetingId?: string;
|
package/src/new-metrics.ts
CHANGED
|
@@ -19,17 +19,19 @@ import {
|
|
|
19
19
|
ClientEvent,
|
|
20
20
|
FeatureEvent,
|
|
21
21
|
EventPayload,
|
|
22
|
-
OperationalEvent,
|
|
23
22
|
MediaQualityEvent,
|
|
24
23
|
InternalEvent,
|
|
25
24
|
SubmitClientEventOptions,
|
|
26
25
|
Table,
|
|
27
26
|
DelayedClientEvent,
|
|
28
27
|
DelayedClientFeatureEvent,
|
|
28
|
+
PrivacyAndSecurityPermission,
|
|
29
29
|
} from './metrics.types';
|
|
30
30
|
import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
|
|
31
31
|
import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
|
|
32
32
|
import {generateCommonErrorMetadata} from './utils';
|
|
33
|
+
import {isAutomatedUser as detectAutomatedUser} from './automated-user';
|
|
34
|
+
import PrivacyAndSecurityPermissionEnricher from './privacy-and-security-permission-enricher';
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* Metrics plugin to centralize all types of metrics.
|
|
@@ -67,6 +69,8 @@ class Metrics extends WebexPlugin {
|
|
|
67
69
|
|
|
68
70
|
delayedClientFeatureEventsOverrides: Partial<DelayedClientFeatureEvent['options']> = {};
|
|
69
71
|
|
|
72
|
+
private privacyAndSecurityPermissionEnricher: PrivacyAndSecurityPermissionEnricher;
|
|
73
|
+
|
|
70
74
|
/**
|
|
71
75
|
* Constructor
|
|
72
76
|
* @param args
|
|
@@ -77,8 +81,19 @@ class Metrics extends WebexPlugin {
|
|
|
77
81
|
constructor(...args) {
|
|
78
82
|
super(...args);
|
|
79
83
|
|
|
84
|
+
this.privacyAndSecurityPermissionEnricher = new PrivacyAndSecurityPermissionEnricher(
|
|
85
|
+
(error) => {
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
this.webex.logger.error(
|
|
88
|
+
'NewMetrics: @submitClientEvent. Privacy and security permission enrichment failed.',
|
|
89
|
+
error
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
);
|
|
80
93
|
// @ts-ignore
|
|
81
94
|
this.callDiagnosticLatencies = new CallDiagnosticLatencies({}, {parent: this.webex});
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
|
|
82
97
|
this.onReady();
|
|
83
98
|
}
|
|
84
99
|
|
|
@@ -88,8 +103,6 @@ class Metrics extends WebexPlugin {
|
|
|
88
103
|
private onReady() {
|
|
89
104
|
// @ts-ignore
|
|
90
105
|
this.webex.once('ready', () => {
|
|
91
|
-
// @ts-ignore
|
|
92
|
-
this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
|
|
93
106
|
this.preLoginMetrics = new PreLoginMetrics(
|
|
94
107
|
// @ts-ignore
|
|
95
108
|
new PreLoginMetricsBatcher({}, {parent: this.webex}),
|
|
@@ -182,6 +195,13 @@ class Metrics extends WebexPlugin {
|
|
|
182
195
|
return this.businessMetrics?.isReadyToSubmitEvents() ?? false;
|
|
183
196
|
}
|
|
184
197
|
|
|
198
|
+
/**
|
|
199
|
+
* @returns whether the current user agent belongs to an automated user
|
|
200
|
+
*/
|
|
201
|
+
isAutomatedUser() {
|
|
202
|
+
return detectAutomatedUser();
|
|
203
|
+
}
|
|
204
|
+
|
|
185
205
|
/**
|
|
186
206
|
* Behavioral event
|
|
187
207
|
* @param args
|
|
@@ -325,7 +345,7 @@ class Metrics extends WebexPlugin {
|
|
|
325
345
|
payload?: RecursivePartial<FeatureEvent['payload']>;
|
|
326
346
|
options: any;
|
|
327
347
|
}) {
|
|
328
|
-
if (!this.
|
|
348
|
+
if (!this.isReady) {
|
|
329
349
|
// @ts-ignore
|
|
330
350
|
this.webex.logger.log(
|
|
331
351
|
`NewMetrics: @submitFeatureEvent. Attempted to submit before webex.ready. Event name: ${name}`
|
|
@@ -360,7 +380,7 @@ class Metrics extends WebexPlugin {
|
|
|
360
380
|
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
361
381
|
options?: SubmitClientEventOptions;
|
|
362
382
|
}): Promise<any> {
|
|
363
|
-
if (!this.
|
|
383
|
+
if (!this.isReady) {
|
|
364
384
|
// @ts-ignore
|
|
365
385
|
this.webex.logger.log(
|
|
366
386
|
`NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: ${name}`
|
|
@@ -373,14 +393,41 @@ class Metrics extends WebexPlugin {
|
|
|
373
393
|
options: {meetingId: options?.meetingId},
|
|
374
394
|
});
|
|
375
395
|
|
|
376
|
-
|
|
396
|
+
const enrichedPayload = this.privacyAndSecurityPermissionEnricher.enrich({
|
|
377
397
|
name,
|
|
378
398
|
payload,
|
|
399
|
+
scope: this.getPermissionScope(options),
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
return this.callDiagnosticMetrics.submitClientEvent({
|
|
403
|
+
name,
|
|
404
|
+
payload: enrichedPayload,
|
|
379
405
|
options,
|
|
380
406
|
delaySubmitEvent: this.delaySubmitClientEvents,
|
|
381
407
|
});
|
|
382
408
|
}
|
|
383
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Stores the latest browser permission state supplied by the client.
|
|
412
|
+
* @param permission latest normalized permission snapshot
|
|
413
|
+
*/
|
|
414
|
+
public setPrivacyAndSecurityPermission(permission: PrivacyAndSecurityPermission): void {
|
|
415
|
+
this.privacyAndSecurityPermissionEnricher.setPermission(permission);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Resolves event options to the call identity used by Call Diagnostic where possible.
|
|
420
|
+
* @param options client event options
|
|
421
|
+
* @returns the permission history scope
|
|
422
|
+
*/
|
|
423
|
+
private getPermissionScope(options?: SubmitClientEventOptions): string {
|
|
424
|
+
const meeting = options?.meetingId
|
|
425
|
+
? (this as any).webex.meetings?.getBasicMeetingInformation?.(options.meetingId)
|
|
426
|
+
: undefined;
|
|
427
|
+
|
|
428
|
+
return options?.correlationId ?? meeting?.correlationId ?? 'default';
|
|
429
|
+
}
|
|
430
|
+
|
|
384
431
|
/**
|
|
385
432
|
* Issue request to alias a user's pre-login ID with their CI UUID
|
|
386
433
|
* @param {string} preLoginId
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {ClientEvent} from '../metrics.types';
|
|
2
|
+
import type {PermissionEnrichmentPolicy} from './types';
|
|
3
|
+
|
|
4
|
+
export const CAMERA_AND_MICROPHONE_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
5
|
+
'client.call.initiated',
|
|
6
|
+
'client.media.capabilities',
|
|
7
|
+
'client.ice.end',
|
|
8
|
+
'client.locus.join.request',
|
|
9
|
+
'client.locus.join.response',
|
|
10
|
+
'client.media-engine.ready',
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export const MEDIA_TX_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
14
|
+
'client.media.tx.start',
|
|
15
|
+
'client.media.tx.stop',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export const CONTENT_SHARE_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
19
|
+
'client.share.initiated',
|
|
20
|
+
'client.share.floor-grant.request',
|
|
21
|
+
'client.share.floor-granted.local',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
export const FINAL_PERMISSION_EVENTS = new Set<ClientEvent['name']>([
|
|
25
|
+
'client.call.leave',
|
|
26
|
+
'client.call.remote-ended',
|
|
27
|
+
'client.call.aborted',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
export const NO_PERMISSION_ENRICHMENT: PermissionEnrichmentPolicy = {
|
|
31
|
+
resources: [],
|
|
32
|
+
terminal: false,
|
|
33
|
+
};
|