@webex/internal-plugin-metrics 3.0.0-beta.42 → 3.0.0-beta.421
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 +40 -1
- package/dist/batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +65 -0
- package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -0
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +552 -0
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.js +863 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +371 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -0
- package/dist/call-diagnostic/config.js +627 -0
- package/dist/call-diagnostic/config.js.map +1 -0
- package/dist/client-metrics-batcher.js +2 -1
- package/dist/client-metrics-batcher.js.map +1 -1
- package/dist/client-metrics-prelogin-batcher.js +33 -0
- package/dist/client-metrics-prelogin-batcher.js.map +1 -0
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +30 -50
- package/dist/metrics.js.map +1 -1
- package/dist/metrics.types.js +7 -0
- package/dist/metrics.types.js.map +1 -0
- package/dist/new-metrics.js +300 -0
- package/dist/new-metrics.js.map +1 -0
- package/dist/prelogin-metrics-batcher.js +82 -0
- package/dist/prelogin-metrics-batcher.js.map +1 -0
- package/dist/types/batcher.d.ts +7 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics-batcher.d.ts +2 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +237 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +425 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +103 -0
- package/dist/types/call-diagnostic/config.d.ts +178 -0
- package/dist/types/client-metrics-batcher.d.ts +2 -0
- package/dist/types/client-metrics-prelogin-batcher.d.ts +2 -0
- package/dist/types/config.d.ts +36 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/metrics.d.ts +3 -0
- package/dist/types/metrics.types.d.ts +107 -0
- package/dist/types/new-metrics.d.ts +131 -0
- package/dist/types/prelogin-metrics-batcher.d.ts +2 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/utils.js +27 -0
- package/dist/utils.js.map +1 -0
- package/package.json +15 -8
- package/src/batcher.js +38 -0
- package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +72 -0
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +511 -0
- package/src/call-diagnostic/call-diagnostic-metrics.ts +925 -0
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +399 -0
- package/src/call-diagnostic/config.ts +685 -0
- package/src/client-metrics-batcher.js +1 -0
- package/src/client-metrics-prelogin-batcher.ts +26 -0
- package/src/config.js +1 -0
- package/src/index.ts +56 -0
- package/src/metrics.js +26 -47
- package/src/metrics.types.ts +179 -0
- package/src/new-metrics.ts +278 -0
- package/src/prelogin-metrics-batcher.ts +95 -0
- package/src/utils.ts +17 -0
- package/test/unit/spec/batcher.js +2 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +469 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +718 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +2371 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +637 -0
- package/test/unit/spec/client-metrics-batcher.js +2 -0
- package/test/unit/spec/client-metrics-prelogin-batcher.ts +54 -0
- package/test/unit/spec/metrics.js +78 -129
- package/test/unit/spec/new-metrics.ts +231 -0
- package/test/unit/spec/prelogin-metrics-batcher.ts +253 -0
- package/test/unit/spec/utils.ts +22 -0
- package/tsconfig.json +6 -0
- package/dist/call-diagnostic-events-batcher.js +0 -60
- package/dist/call-diagnostic-events-batcher.js.map +0 -1
- package/src/call-diagnostic-events-batcher.js +0 -62
- package/src/index.js +0 -17
- package/test/unit/spec/call-diagnostic-events-batcher.js +0 -195
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import PreLoginMetricsBatcher from './prelogin-metrics-batcher';
|
|
2
|
+
|
|
3
|
+
const ClientMetricsPreloginBatcher = PreLoginMetricsBatcher.extend({
|
|
4
|
+
namespace: 'Metrics',
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Prepare item
|
|
8
|
+
* @param {any} item
|
|
9
|
+
* @returns {Promise<any>}
|
|
10
|
+
*/
|
|
11
|
+
prepareItem(item) {
|
|
12
|
+
// Add more defaults to payload when the clientmetrics endpoint evolves to support richer payloads
|
|
13
|
+
return Promise.resolve(item);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Prepare request, add time sensitive date etc.
|
|
18
|
+
* @param {any[]} queue
|
|
19
|
+
* @returns {Promise<any[]>}
|
|
20
|
+
*/
|
|
21
|
+
prepareRequest(queue) {
|
|
22
|
+
return Promise.resolve(queue);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export default ClientMetricsPreloginBatcher;
|
package/src/config.js
CHANGED
package/src/index.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import '@webex/internal-plugin-device';
|
|
6
|
+
|
|
7
|
+
import {registerInternalPlugin} from '@webex/webex-core';
|
|
8
|
+
|
|
9
|
+
import Metrics from './metrics';
|
|
10
|
+
import config from './config';
|
|
11
|
+
import NewMetrics from './new-metrics';
|
|
12
|
+
import * as Utils from './utils';
|
|
13
|
+
import {
|
|
14
|
+
ClientEvent,
|
|
15
|
+
ClientEventLeaveReason,
|
|
16
|
+
SubmitBehavioralEvent,
|
|
17
|
+
SubmitClientEvent,
|
|
18
|
+
SubmitInternalEvent,
|
|
19
|
+
SubmitOperationalEvent,
|
|
20
|
+
SubmitMQE,
|
|
21
|
+
PreComputedLatencies,
|
|
22
|
+
} from './metrics.types';
|
|
23
|
+
import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
24
|
+
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
25
|
+
import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
|
|
26
|
+
import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
|
|
27
|
+
|
|
28
|
+
registerInternalPlugin('metrics', Metrics, {
|
|
29
|
+
config,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
registerInternalPlugin('newMetrics', NewMetrics, {
|
|
33
|
+
config,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export {default, getOSNameInternal} from './metrics';
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
config,
|
|
40
|
+
CALL_DIAGNOSTIC_CONFIG,
|
|
41
|
+
NewMetrics,
|
|
42
|
+
Utils,
|
|
43
|
+
CallDiagnosticUtils,
|
|
44
|
+
CallDiagnosticLatencies,
|
|
45
|
+
CallDiagnosticMetrics,
|
|
46
|
+
};
|
|
47
|
+
export type {
|
|
48
|
+
ClientEvent,
|
|
49
|
+
ClientEventLeaveReason,
|
|
50
|
+
SubmitBehavioralEvent,
|
|
51
|
+
SubmitClientEvent,
|
|
52
|
+
SubmitInternalEvent,
|
|
53
|
+
SubmitMQE,
|
|
54
|
+
SubmitOperationalEvent,
|
|
55
|
+
PreComputedLatencies,
|
|
56
|
+
};
|
package/src/metrics.js
CHANGED
|
@@ -10,7 +10,7 @@ import {OS_NAME, OSMap, CLIENT_NAME} from './config';
|
|
|
10
10
|
|
|
11
11
|
import Batcher from './batcher';
|
|
12
12
|
import ClientMetricsBatcher from './client-metrics-batcher';
|
|
13
|
-
import
|
|
13
|
+
import ClientMetricsPreloginBatcher from './client-metrics-prelogin-batcher';
|
|
14
14
|
|
|
15
15
|
const {getOSName, getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
|
|
16
16
|
|
|
@@ -38,7 +38,7 @@ const Metrics = WebexPlugin.extend({
|
|
|
38
38
|
children: {
|
|
39
39
|
batcher: Batcher,
|
|
40
40
|
clientMetricsBatcher: ClientMetricsBatcher,
|
|
41
|
-
|
|
41
|
+
clientMetricsPreloginBatcher: ClientMetricsPreloginBatcher,
|
|
42
42
|
},
|
|
43
43
|
|
|
44
44
|
namespace: 'Metrics',
|
|
@@ -48,37 +48,31 @@ const Metrics = WebexPlugin.extend({
|
|
|
48
48
|
},
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* Returns the payload for submitting client metrics.
|
|
52
52
|
* @param {string} eventName
|
|
53
|
-
* @param {
|
|
54
|
-
* @
|
|
55
|
-
* @returns {Object} HttpResponse object
|
|
53
|
+
* @param {any} props
|
|
54
|
+
* @returns {any} - the payload
|
|
56
55
|
*/
|
|
57
|
-
|
|
56
|
+
getClientMetricsPayload(eventName, props) {
|
|
58
57
|
if (!eventName) {
|
|
59
58
|
throw Error('Missing behavioral metric name. Please provide one');
|
|
60
59
|
}
|
|
61
60
|
const payload = {metricName: eventName};
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
const providedClientVersion = this.webex.meetings?.config?.metrics?.clientVersion;
|
|
62
63
|
|
|
63
64
|
payload.tags = {
|
|
64
65
|
...props.tags,
|
|
65
66
|
browser: getBrowserName(),
|
|
66
67
|
os: getOSNameInternal(),
|
|
68
|
+
appVersion: providedClientVersion,
|
|
67
69
|
|
|
68
70
|
// Node does not like this so we need to check if it exists or not
|
|
69
71
|
// eslint-disable-next-line no-undef
|
|
70
72
|
domain:
|
|
71
73
|
typeof window !== 'undefined' ? window.location.hostname || 'non-browser' : 'non-browser', // Check what else we could measure
|
|
72
|
-
client_id: this.webex.credentials.config.client_id,
|
|
73
|
-
user_id: this.webex.internal.device.userId,
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
try {
|
|
77
|
-
payload.tags.org_id = this.webex.credentials.getOrgId();
|
|
78
|
-
} catch {
|
|
79
|
-
this.logger.info('metrics: unable to get orgId');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
76
|
payload.fields = {
|
|
83
77
|
...props.fields,
|
|
84
78
|
browser_version: getBrowserVersion(),
|
|
@@ -86,6 +80,7 @@ const Metrics = WebexPlugin.extend({
|
|
|
86
80
|
sdk_version: this.webex.version,
|
|
87
81
|
platform: 'Web',
|
|
88
82
|
spark_user_agent: getSparkUserAgent(this.webex),
|
|
83
|
+
client_id: this.webex.credentials.config.client_id,
|
|
89
84
|
};
|
|
90
85
|
|
|
91
86
|
payload.type = props.type || this.webex.config.metrics.type;
|
|
@@ -110,15 +105,23 @@ const Metrics = WebexPlugin.extend({
|
|
|
110
105
|
// is impossible so unable to use Date.now()
|
|
111
106
|
payload.timestamp = new Date().valueOf();
|
|
112
107
|
|
|
108
|
+
return payload;
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* This corresponds to #sendSemiStructured() in the deprecated metrics handler
|
|
113
|
+
* @param {string} eventName
|
|
114
|
+
* @param {Object} props
|
|
115
|
+
* @param {string} preLoginId
|
|
116
|
+
* @returns {Object} HttpResponse object
|
|
117
|
+
*/
|
|
118
|
+
submitClientMetrics(eventName, props = {}, preLoginId) {
|
|
119
|
+
const payload = this.getClientMetricsPayload(eventName, props);
|
|
120
|
+
|
|
113
121
|
if (preLoginId) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Do not batch these because pre-login events occur during onboarding, so we will be partially blind
|
|
119
|
-
// to users' progress through the reg flow if we wait to persist pre-login metrics for people who drop off because
|
|
120
|
-
// their metrics will not post from a queue flush in time
|
|
121
|
-
return this.postPreLoginMetric(_payload, preLoginId);
|
|
122
|
+
this.clientMetricsPreloginBatcher.savePreLoginId(preLoginId);
|
|
123
|
+
|
|
124
|
+
return this.clientMetricsPreloginBatcher.request(payload);
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
return this.clientMetricsBatcher.request(payload);
|
|
@@ -143,30 +146,6 @@ const Metrics = WebexPlugin.extend({
|
|
|
143
146
|
},
|
|
144
147
|
});
|
|
145
148
|
},
|
|
146
|
-
|
|
147
|
-
postPreLoginMetric(payload, preLoginId) {
|
|
148
|
-
return this.webex.credentials.getClientToken().then((token) =>
|
|
149
|
-
this.request({
|
|
150
|
-
method: 'POST',
|
|
151
|
-
api: 'metrics',
|
|
152
|
-
resource: 'clientmetrics-prelogin',
|
|
153
|
-
headers: {
|
|
154
|
-
authorization: token.toString(),
|
|
155
|
-
'x-prelogin-userid': preLoginId,
|
|
156
|
-
},
|
|
157
|
-
body: payload,
|
|
158
|
-
})
|
|
159
|
-
);
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
submitCallDiagnosticEvents(payload) {
|
|
163
|
-
const event = {
|
|
164
|
-
type: 'diagnostic-event',
|
|
165
|
-
eventPayload: payload,
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
return this.callDiagnosticEventsBatcher.request(event);
|
|
169
|
-
},
|
|
170
149
|
});
|
|
171
150
|
|
|
172
151
|
export default Metrics;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientEvent as RawClientEvent,
|
|
3
|
+
Event as RawEvent,
|
|
4
|
+
MediaQualityEvent as RawMediaQualityEvent,
|
|
5
|
+
} from '@webex/event-dictionary-ts';
|
|
6
|
+
|
|
7
|
+
export type Event = Omit<RawEvent, 'event'> & {event: RawClientEvent | RawMediaQualityEvent};
|
|
8
|
+
|
|
9
|
+
export type ClientEventError = NonNullable<RawClientEvent['errors']>[0];
|
|
10
|
+
|
|
11
|
+
export type EnvironmentType = NonNullable<RawEvent['origin']['environment']>;
|
|
12
|
+
|
|
13
|
+
export type NewEnvironmentType = NonNullable<RawEvent['origin']['newEnvironment']>;
|
|
14
|
+
|
|
15
|
+
export type ClientLaunchMethodType = NonNullable<
|
|
16
|
+
RawEvent['origin']['clientInfo']
|
|
17
|
+
>['clientLaunchMethod'];
|
|
18
|
+
|
|
19
|
+
export type BrowserLaunchMethodType = NonNullable<
|
|
20
|
+
RawEvent['origin']['clientInfo']
|
|
21
|
+
>['browserLaunchMethod'];
|
|
22
|
+
|
|
23
|
+
export type SubmitClientEventOptions = {
|
|
24
|
+
meetingId?: string;
|
|
25
|
+
mediaConnections?: any[];
|
|
26
|
+
rawError?: any;
|
|
27
|
+
correlationId?: string;
|
|
28
|
+
preLoginId?: string;
|
|
29
|
+
environment?: EnvironmentType;
|
|
30
|
+
newEnvironmentType?: NewEnvironmentType;
|
|
31
|
+
clientLaunchMethod?: ClientLaunchMethodType;
|
|
32
|
+
browserLaunchMethod?: BrowserLaunchMethodType;
|
|
33
|
+
webexConferenceIdStr?: string;
|
|
34
|
+
globalMeetingId?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type SubmitMQEOptions = {
|
|
38
|
+
meetingId: string;
|
|
39
|
+
mediaConnections?: any[];
|
|
40
|
+
networkType?: Event['origin']['networkType'];
|
|
41
|
+
webexConferenceIdStr?: string;
|
|
42
|
+
globalMeetingId?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type InternalEvent = {
|
|
46
|
+
name:
|
|
47
|
+
| 'internal.client.meetinginfo.request'
|
|
48
|
+
| 'internal.client.meetinginfo.response'
|
|
49
|
+
| 'internal.register.device.request'
|
|
50
|
+
| 'internal.register.device.response'
|
|
51
|
+
| 'internal.reset.join.latencies'
|
|
52
|
+
| 'internal.client.meeting.click.joinbutton'
|
|
53
|
+
| 'internal.host.meeting.participant.admitted'
|
|
54
|
+
| 'internal.client.meeting.interstitial-window.showed'
|
|
55
|
+
| 'internal.client.interstitial-window.click.joinbutton'
|
|
56
|
+
| 'internal.client.add-media.turn-discovery.start'
|
|
57
|
+
| 'internal.client.add-media.turn-discovery.end';
|
|
58
|
+
|
|
59
|
+
payload?: never;
|
|
60
|
+
options?: never;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export interface ClientEvent {
|
|
64
|
+
name: RawClientEvent['name'];
|
|
65
|
+
payload?: RawClientEvent;
|
|
66
|
+
options?: SubmitClientEventOptions;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface BehavioralEvent {
|
|
70
|
+
// TODO: not implemented
|
|
71
|
+
name: 'host.meeting.participant.admitted' | 'sdk.media-flow.started';
|
|
72
|
+
payload?: never;
|
|
73
|
+
options?: never;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface OperationalEvent {
|
|
77
|
+
// TODO: not implemented
|
|
78
|
+
name: never;
|
|
79
|
+
payload?: never;
|
|
80
|
+
options?: never;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface FeatureEvent {
|
|
84
|
+
// TODO: not implemented
|
|
85
|
+
name: never;
|
|
86
|
+
payload?: never;
|
|
87
|
+
options?: never;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface MediaQualityEvent {
|
|
91
|
+
name: RawMediaQualityEvent['name'];
|
|
92
|
+
payload?: RawMediaQualityEvent;
|
|
93
|
+
options: SubmitMQEOptions;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type RecursivePartial<T> = {
|
|
97
|
+
[P in keyof T]?: T[P] extends (infer U)[]
|
|
98
|
+
? RecursivePartial<U>[]
|
|
99
|
+
: T[P] extends object
|
|
100
|
+
? RecursivePartial<T[P]>
|
|
101
|
+
: T[P];
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export type MetricEventNames =
|
|
105
|
+
| InternalEvent['name']
|
|
106
|
+
| ClientEvent['name']
|
|
107
|
+
| BehavioralEvent['name']
|
|
108
|
+
| OperationalEvent['name']
|
|
109
|
+
| FeatureEvent['name']
|
|
110
|
+
| MediaQualityEvent['name'];
|
|
111
|
+
|
|
112
|
+
export type ClientInfo = NonNullable<RawEvent['origin']['clientInfo']>;
|
|
113
|
+
export type ClientType = NonNullable<RawEvent['origin']['clientInfo']>['clientType'];
|
|
114
|
+
export type SubClientType = NonNullable<RawEvent['origin']['clientInfo']>['subClientType'];
|
|
115
|
+
export type NetworkType = NonNullable<RawEvent['origin']>['networkType'];
|
|
116
|
+
|
|
117
|
+
export type ClientSubServiceType = ClientEvent['payload']['webexSubServiceType'];
|
|
118
|
+
export type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;
|
|
119
|
+
export type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];
|
|
120
|
+
export type ClientEventPayloadError = ClientEvent['payload']['errors'];
|
|
121
|
+
|
|
122
|
+
export type MediaQualityEventAudioSetupDelayPayload = NonNullable<
|
|
123
|
+
MediaQualityEvent['payload']
|
|
124
|
+
>['audioSetupDelay'];
|
|
125
|
+
export type MediaQualityEventVideoSetupDelayPayload = NonNullable<
|
|
126
|
+
MediaQualityEvent['payload']
|
|
127
|
+
>['videoSetupDelay'];
|
|
128
|
+
|
|
129
|
+
export type SubmitMQEPayload = RecursivePartial<MediaQualityEvent['payload']> & {
|
|
130
|
+
intervals: NonNullable<MediaQualityEvent['payload']>['intervals'];
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type SubmitInternalEvent = (args: {
|
|
134
|
+
name: InternalEvent['name'];
|
|
135
|
+
payload?: RecursivePartial<InternalEvent['payload']>;
|
|
136
|
+
options?: any;
|
|
137
|
+
}) => void;
|
|
138
|
+
|
|
139
|
+
export type SubmitBehavioralEvent = (args: {
|
|
140
|
+
name: BehavioralEvent['name'];
|
|
141
|
+
payload?: RecursivePartial<BehavioralEvent['payload']>;
|
|
142
|
+
options?: any;
|
|
143
|
+
}) => void;
|
|
144
|
+
|
|
145
|
+
export type SubmitClientEvent = (args: {
|
|
146
|
+
name: ClientEvent['name'];
|
|
147
|
+
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
148
|
+
options?: SubmitClientEventOptions;
|
|
149
|
+
}) => Promise<any>;
|
|
150
|
+
|
|
151
|
+
export type SubmitOperationalEvent = (args: {
|
|
152
|
+
name: OperationalEvent['name'];
|
|
153
|
+
payload?: RecursivePartial<OperationalEvent['payload']>;
|
|
154
|
+
options?: any;
|
|
155
|
+
}) => void;
|
|
156
|
+
|
|
157
|
+
export type SubmitMQE = (args: {
|
|
158
|
+
name: MediaQualityEvent['name'];
|
|
159
|
+
payload: SubmitMQEPayload;
|
|
160
|
+
options: any;
|
|
161
|
+
}) => void;
|
|
162
|
+
|
|
163
|
+
export type BuildClientEventFetchRequestOptions = (args: {
|
|
164
|
+
name: ClientEvent['name'];
|
|
165
|
+
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
166
|
+
options?: SubmitClientEventOptions;
|
|
167
|
+
}) => Promise<any>;
|
|
168
|
+
|
|
169
|
+
export type PreComputedLatencies =
|
|
170
|
+
| 'internal.client.pageJMT'
|
|
171
|
+
| 'internal.download.time'
|
|
172
|
+
| 'internal.get.cluster.time'
|
|
173
|
+
| 'internal.click.to.interstitial'
|
|
174
|
+
| 'internal.refresh.captcha.time'
|
|
175
|
+
| 'internal.exchange.ci.token.time'
|
|
176
|
+
| 'internal.get.u2c.time'
|
|
177
|
+
| 'internal.call.init.join.req'
|
|
178
|
+
| 'internal.other.app.api.time'
|
|
179
|
+
| 'internal.api.fetch.intelligence.models';
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable class-methods-use-this */
|
|
3
|
+
/* eslint-disable valid-jsdoc */
|
|
4
|
+
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import {WebexPlugin} from '@webex/webex-core';
|
|
7
|
+
|
|
8
|
+
import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
|
|
9
|
+
import {
|
|
10
|
+
RecursivePartial,
|
|
11
|
+
ClientEvent,
|
|
12
|
+
FeatureEvent,
|
|
13
|
+
BehavioralEvent,
|
|
14
|
+
OperationalEvent,
|
|
15
|
+
MediaQualityEvent,
|
|
16
|
+
InternalEvent,
|
|
17
|
+
SubmitClientEventOptions,
|
|
18
|
+
} from './metrics.types';
|
|
19
|
+
import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';
|
|
20
|
+
import {setMetricTimings} from './call-diagnostic/call-diagnostic-metrics.util';
|
|
21
|
+
import {generateCommonErrorMetadata} from './utils';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Metrics plugin to centralize all types of metrics.
|
|
25
|
+
* @class
|
|
26
|
+
*/
|
|
27
|
+
class Metrics extends WebexPlugin {
|
|
28
|
+
// eslint-disable-next-line no-use-before-define
|
|
29
|
+
static instance: Metrics;
|
|
30
|
+
|
|
31
|
+
// Call Diagnostic latencies
|
|
32
|
+
callDiagnosticLatencies: CallDiagnosticLatencies;
|
|
33
|
+
// Helper classes to handle the different types of metrics
|
|
34
|
+
callDiagnosticMetrics: CallDiagnosticMetrics;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Constructor
|
|
38
|
+
* @param args
|
|
39
|
+
* @constructor
|
|
40
|
+
* @private
|
|
41
|
+
* @returns
|
|
42
|
+
*/
|
|
43
|
+
constructor(...args) {
|
|
44
|
+
super(...args);
|
|
45
|
+
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
this.callDiagnosticLatencies = new CallDiagnosticLatencies({}, {parent: this.webex});
|
|
48
|
+
this.onReady();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* On Ready
|
|
53
|
+
*/
|
|
54
|
+
private onReady() {
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
this.webex.once('ready', () => {
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Used for internal purposes only
|
|
64
|
+
* @param args
|
|
65
|
+
*/
|
|
66
|
+
submitInternalEvent({
|
|
67
|
+
name,
|
|
68
|
+
payload,
|
|
69
|
+
options,
|
|
70
|
+
}: {
|
|
71
|
+
name: InternalEvent['name'];
|
|
72
|
+
payload?: RecursivePartial<InternalEvent['payload']>;
|
|
73
|
+
options?: any;
|
|
74
|
+
}) {
|
|
75
|
+
if (name === 'internal.reset.join.latencies') {
|
|
76
|
+
this.callDiagnosticLatencies.clearTimestamps();
|
|
77
|
+
} else {
|
|
78
|
+
this.callDiagnosticLatencies.saveTimestamp({key: name});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Behavioral event
|
|
84
|
+
* @param args
|
|
85
|
+
*/
|
|
86
|
+
submitBehavioralEvent({
|
|
87
|
+
name,
|
|
88
|
+
payload,
|
|
89
|
+
options,
|
|
90
|
+
}: {
|
|
91
|
+
name: BehavioralEvent['name'];
|
|
92
|
+
payload?: RecursivePartial<BehavioralEvent['payload']>;
|
|
93
|
+
options?: any;
|
|
94
|
+
}) {
|
|
95
|
+
this.callDiagnosticLatencies.saveTimestamp({key: name});
|
|
96
|
+
throw new Error('Not implemented.');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Operational event
|
|
101
|
+
* @param args
|
|
102
|
+
*/
|
|
103
|
+
submitOperationalEvent({
|
|
104
|
+
name,
|
|
105
|
+
payload,
|
|
106
|
+
options,
|
|
107
|
+
}: {
|
|
108
|
+
name: OperationalEvent['name'];
|
|
109
|
+
payload?: RecursivePartial<OperationalEvent['payload']>;
|
|
110
|
+
options?: any;
|
|
111
|
+
}) {
|
|
112
|
+
throw new Error('Not implemented.');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Call Analyzer: Media Quality Event
|
|
117
|
+
* @param args
|
|
118
|
+
*/
|
|
119
|
+
submitMQE({
|
|
120
|
+
name,
|
|
121
|
+
payload,
|
|
122
|
+
options,
|
|
123
|
+
}: {
|
|
124
|
+
name: MediaQualityEvent['name'];
|
|
125
|
+
payload: RecursivePartial<MediaQualityEvent['payload']> & {
|
|
126
|
+
intervals: MediaQualityEvent['payload']['intervals'];
|
|
127
|
+
};
|
|
128
|
+
options: any;
|
|
129
|
+
}) {
|
|
130
|
+
this.callDiagnosticLatencies.saveTimestamp({key: name});
|
|
131
|
+
this.callDiagnosticMetrics.submitMQE({name, payload, options});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Call Analyzer: Feature Usage Event
|
|
136
|
+
* @param args
|
|
137
|
+
*/
|
|
138
|
+
submitFeatureEvent({
|
|
139
|
+
name,
|
|
140
|
+
payload,
|
|
141
|
+
options,
|
|
142
|
+
}: {
|
|
143
|
+
name: FeatureEvent['name'];
|
|
144
|
+
payload?: RecursivePartial<FeatureEvent['payload']>;
|
|
145
|
+
options: any;
|
|
146
|
+
}) {
|
|
147
|
+
throw new Error('Not implemented.');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Call Analyzer: Client Event
|
|
152
|
+
* @public
|
|
153
|
+
* @param args
|
|
154
|
+
*/
|
|
155
|
+
public submitClientEvent({
|
|
156
|
+
name,
|
|
157
|
+
payload,
|
|
158
|
+
options,
|
|
159
|
+
}: {
|
|
160
|
+
name: ClientEvent['name'];
|
|
161
|
+
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
162
|
+
options?: SubmitClientEventOptions;
|
|
163
|
+
}): Promise<any> {
|
|
164
|
+
if (!this.callDiagnosticLatencies || !this.callDiagnosticMetrics) {
|
|
165
|
+
// @ts-ignore
|
|
166
|
+
this.webex.logger.log(
|
|
167
|
+
`NewMetrics: @submitClientEvent. Attempted to submit before webex.ready. Event name: ${name}`
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return Promise.resolve();
|
|
171
|
+
}
|
|
172
|
+
this.callDiagnosticLatencies.saveTimestamp({
|
|
173
|
+
key: name,
|
|
174
|
+
options: {meetingId: options?.meetingId},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return this.callDiagnosticMetrics.submitClientEvent({name, payload, options});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Issue request to alias a user's pre-login ID with their CI UUID
|
|
182
|
+
* @param {string} preLoginId
|
|
183
|
+
* @returns {Object} HttpResponse object
|
|
184
|
+
*/
|
|
185
|
+
public clientMetricsAliasUser(preLoginId: string) {
|
|
186
|
+
// @ts-ignore
|
|
187
|
+
return this.webex
|
|
188
|
+
.request({
|
|
189
|
+
method: 'POST',
|
|
190
|
+
api: 'metrics',
|
|
191
|
+
resource: 'clientmetrics',
|
|
192
|
+
headers: {
|
|
193
|
+
'x-prelogin-userid': preLoginId,
|
|
194
|
+
},
|
|
195
|
+
body: {},
|
|
196
|
+
qs: {
|
|
197
|
+
alias: true,
|
|
198
|
+
},
|
|
199
|
+
})
|
|
200
|
+
.then((res) => {
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
this.webex.logger.log(`NewMetrics: @clientMetricsAliasUser. Request successful.`);
|
|
203
|
+
|
|
204
|
+
return res;
|
|
205
|
+
})
|
|
206
|
+
.catch((err) => {
|
|
207
|
+
// @ts-ignore
|
|
208
|
+
this.logger.error(
|
|
209
|
+
`NewMetrics: @clientMetricsAliasUser. Request failed:`,
|
|
210
|
+
`err: ${generateCommonErrorMetadata(err)}`
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return Promise.reject(err);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Returns a promise that will resolve to fetch options for submitting a metric.
|
|
219
|
+
*
|
|
220
|
+
* This is to support quickly submitting metrics when the browser/tab is closing.
|
|
221
|
+
* Calling submitClientEvent will not work because there some async steps that will
|
|
222
|
+
* not complete before the browser is closed. Instead, we pre-gather all the
|
|
223
|
+
* information/options needed for the request(s), and then simply and quickly
|
|
224
|
+
* fire the fetch(es) when beforeUnload is triggered.
|
|
225
|
+
*
|
|
226
|
+
* We must use fetch instead of request because fetch has a keepalive option that
|
|
227
|
+
* allows the request it to outlive the page.
|
|
228
|
+
*
|
|
229
|
+
* Note: the timings values will be wrong, but setMetricTimingsAndFetch() will
|
|
230
|
+
* properly adjust them before submitting.
|
|
231
|
+
*
|
|
232
|
+
* @public
|
|
233
|
+
* @param {Object} arg
|
|
234
|
+
* @param {String} arg.name - event name
|
|
235
|
+
* @param {Object} arg.payload - event payload
|
|
236
|
+
* @param {Object} arg.options - other options
|
|
237
|
+
* @returns {Promise} promise that resolves to options to be used with fetch
|
|
238
|
+
*/
|
|
239
|
+
public async buildClientEventFetchRequestOptions({
|
|
240
|
+
name,
|
|
241
|
+
payload,
|
|
242
|
+
options,
|
|
243
|
+
}: {
|
|
244
|
+
name: ClientEvent['name'];
|
|
245
|
+
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
246
|
+
options?: SubmitClientEventOptions;
|
|
247
|
+
}): Promise<any> {
|
|
248
|
+
return this.callDiagnosticMetrics.buildClientEventFetchRequestOptions({
|
|
249
|
+
name,
|
|
250
|
+
payload,
|
|
251
|
+
options,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Submits a metric from pre-built request options via the fetch API. Updates
|
|
257
|
+
* the "$timings" and "originTime" values to Date.now() since the existing times
|
|
258
|
+
* were set when the options were built (not submitted).
|
|
259
|
+
|
|
260
|
+
* @param {any} options - the pre-built request options for submitting a metric
|
|
261
|
+
* @returns {Promise} promise that resolves to the response object
|
|
262
|
+
*/
|
|
263
|
+
public setMetricTimingsAndFetch(options: any): Promise<any> {
|
|
264
|
+
// @ts-ignore
|
|
265
|
+
return this.webex.setTimingsAndFetch(setMetricTimings(options));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Returns true if the specified serviceErrorCode maps to an expected error.
|
|
270
|
+
* @param {number} serviceErrorCode the service error code
|
|
271
|
+
* @returns {boolean}
|
|
272
|
+
*/
|
|
273
|
+
public isServiceErrorExpected(serviceErrorCode: number): boolean {
|
|
274
|
+
return this.callDiagnosticMetrics.isServiceErrorExpected(serviceErrorCode);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export default Metrics;
|