@webex/internal-plugin-metrics 3.4.0 → 3.5.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/behavioral-metrics.js +63 -0
  2. package/dist/behavioral-metrics.js.map +1 -0
  3. package/dist/business-metrics.js +62 -0
  4. package/dist/business-metrics.js.map +1 -0
  5. package/dist/call-diagnostic/call-diagnostic-metrics.js +15 -5
  6. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  7. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +14 -4
  8. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  9. package/dist/{behavioral/behavioral-metrics.js → generic-metrics.js} +77 -92
  10. package/dist/generic-metrics.js.map +1 -0
  11. package/dist/index.js +15 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/metrics.js +1 -1
  14. package/dist/metrics.types.js.map +1 -1
  15. package/dist/new-metrics.js +122 -24
  16. package/dist/new-metrics.js.map +1 -1
  17. package/dist/operational-metrics.js +56 -0
  18. package/dist/operational-metrics.js.map +1 -0
  19. package/dist/types/behavioral-metrics.d.ts +25 -0
  20. package/dist/types/business-metrics.d.ts +19 -0
  21. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +21 -5
  22. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +2 -1
  23. package/dist/types/generic-metrics.d.ts +63 -0
  24. package/dist/types/index.d.ts +4 -2
  25. package/dist/types/metrics.types.d.ts +26 -14
  26. package/dist/types/new-metrics.d.ts +41 -9
  27. package/dist/types/operational-metrics.d.ts +19 -0
  28. package/package.json +12 -12
  29. package/src/behavioral-metrics.ts +40 -0
  30. package/src/business-metrics.ts +30 -0
  31. package/src/call-diagnostic/call-diagnostic-metrics.ts +19 -2
  32. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +17 -4
  33. package/src/generic-metrics.ts +146 -0
  34. package/src/index.ts +5 -1
  35. package/src/metrics.types.ts +30 -16
  36. package/src/new-metrics.ts +95 -17
  37. package/src/operational-metrics.ts +24 -0
  38. package/test/unit/spec/behavioral/behavioral-metrics.ts +51 -10
  39. package/test/unit/spec/business/business-metrics.ts +120 -0
  40. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +2 -1
  41. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +167 -1
  42. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +22 -8
  43. package/test/unit/spec/new-metrics.ts +14 -0
  44. package/test/unit/spec/operational/operational-metrics.ts +115 -0
  45. package/test/unit/spec/prelogin-metrics-batcher.ts +3 -1
  46. package/dist/behavioral/behavioral-metrics.js.map +0 -1
  47. package/dist/behavioral/config.js +0 -11
  48. package/dist/behavioral/config.js.map +0 -1
  49. package/dist/types/behavioral/behavioral-metrics.d.ts +0 -63
  50. package/dist/types/behavioral/config.d.ts +0 -1
  51. package/src/behavioral/behavioral-metrics.ts +0 -179
  52. package/src/behavioral/config.ts +0 -3
@@ -61,6 +61,7 @@ export type SubmitClientEventOptions = {
61
61
  mediaConnections?: any[];
62
62
  rawError?: any;
63
63
  correlationId?: string;
64
+ sessionCorrelationId?: string;
64
65
  preLoginId?: string;
65
66
  environment?: EnvironmentType;
66
67
  newEnvironmentType?: NewEnvironmentType;
@@ -102,7 +103,7 @@ export interface ClientEvent {
102
103
  options?: SubmitClientEventOptions;
103
104
  }
104
105
 
105
- export interface BehavioralEventContext {
106
+ export interface DeviceContext {
106
107
  app: {version: string};
107
108
  device: {id: string};
108
109
  locale: string;
@@ -112,23 +113,36 @@ export interface BehavioralEventContext {
112
113
  };
113
114
  }
114
115
 
115
- export interface BehavioralEvent {
116
- context: BehavioralEventContext;
116
+ export type MetricType = 'behavioral' | 'operational' | 'business';
117
+
118
+ type InternalEventPayload = string | number | boolean;
119
+ export type EventPayload = Record<string, InternalEventPayload>;
120
+ export type BehavioralEventPayload = EventPayload; // for compatibilty, can be remove after wxcc-desktop did change their imports.
121
+
122
+ export interface BusinessEventPayload {
117
123
  metricName: string;
118
- tags: Record<string, string | number | boolean>;
119
124
  timestamp: number;
120
- type: string[];
125
+ context: DeviceContext;
126
+ browserDetails: EventPayload;
127
+ value: EventPayload;
121
128
  }
122
129
 
123
- export type BehavioralEventPayload = BehavioralEvent['tags'];
130
+ export interface BusinessEvent {
131
+ type: string[];
132
+ eventPayload: BusinessEventPayload;
133
+ }
124
134
 
125
- export interface OperationalEvent {
126
- // TODO: not implemented
127
- name: never;
128
- payload?: never;
129
- options?: never;
135
+ export interface TaggedEvent {
136
+ context: DeviceContext;
137
+ metricName: string;
138
+ tags: EventPayload;
139
+ timestamp: number;
140
+ type: [MetricType];
130
141
  }
131
142
 
143
+ export type BehavioralEvent = TaggedEvent;
144
+ export type OperationalEvent = TaggedEvent;
145
+
132
146
  export interface FeatureEvent {
133
147
  // TODO: not implemented
134
148
  name: never;
@@ -154,7 +168,8 @@ export type MetricEventNames =
154
168
  | InternalEvent['name']
155
169
  | ClientEvent['name']
156
170
  | BehavioralEvent['metricName']
157
- | OperationalEvent['name']
171
+ | OperationalEvent['metricName']
172
+ | BusinessEvent['eventPayload']['metricName']
158
173
  | FeatureEvent['name']
159
174
  | MediaQualityEvent['name'];
160
175
 
@@ -190,7 +205,7 @@ export type SubmitBehavioralEvent = (args: {
190
205
  agent: MetricEventAgent;
191
206
  target: string;
192
207
  verb: MetricEventVerb;
193
- payload?: BehavioralEventPayload;
208
+ payload?: EventPayload;
194
209
  }) => void;
195
210
 
196
211
  export type SubmitClientEvent = (args: {
@@ -200,9 +215,8 @@ export type SubmitClientEvent = (args: {
200
215
  }) => Promise<any>;
201
216
 
202
217
  export type SubmitOperationalEvent = (args: {
203
- name: OperationalEvent['name'];
204
- payload?: RecursivePartial<OperationalEvent['payload']>;
205
- options?: any;
218
+ name: OperationalEvent['metricName'];
219
+ payload: EventPayload;
206
220
  }) => void;
207
221
 
208
222
  export type SubmitMQE = (args: {
@@ -6,7 +6,9 @@
6
6
  import {WebexPlugin} from '@webex/webex-core';
7
7
 
8
8
  import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
9
- import BehavioralMetrics from './behavioral/behavioral-metrics';
9
+ import BehavioralMetrics from './behavioral-metrics';
10
+ import OperationalMetrics from './operational-metrics';
11
+ import BusinessMetrics from './business-metrics';
10
12
  import {
11
13
  RecursivePartial,
12
14
  MetricEventProduct,
@@ -14,7 +16,7 @@ import {
14
16
  MetricEventVerb,
15
17
  ClientEvent,
16
18
  FeatureEvent,
17
- BehavioralEventPayload,
19
+ EventPayload,
18
20
  OperationalEvent,
19
21
  MediaQualityEvent,
20
22
  InternalEvent,
@@ -37,6 +39,9 @@ class Metrics extends WebexPlugin {
37
39
  // Helper classes to handle the different types of metrics
38
40
  callDiagnosticMetrics: CallDiagnosticMetrics;
39
41
  behavioralMetrics: BehavioralMetrics;
42
+ operationalMetrics: OperationalMetrics;
43
+ businessMetrics: BusinessMetrics;
44
+ isReady = false;
40
45
 
41
46
  /**
42
47
  * Constructor
@@ -61,8 +66,7 @@ class Metrics extends WebexPlugin {
61
66
  this.webex.once('ready', () => {
62
67
  // @ts-ignore
63
68
  this.callDiagnosticMetrics = new CallDiagnosticMetrics({}, {parent: this.webex});
64
- // @ts-ignore
65
- this.behavioralMetrics = new BehavioralMetrics({}, {parent: this.webex});
69
+ this.isReady = true;
66
70
  });
67
71
  }
68
72
 
@@ -86,11 +90,61 @@ class Metrics extends WebexPlugin {
86
90
  }
87
91
  }
88
92
 
93
+ /**
94
+ * if webex metrics is ready, build behavioral metric backend if not already done.
95
+ */
96
+ private lazyBuildBehavioralMetrics() {
97
+ if (this.isReady && !this.behavioralMetrics) {
98
+ // @ts-ignore
99
+ this.behavioralMetrics = new BehavioralMetrics({}, {parent: this.webex});
100
+ }
101
+ }
102
+
103
+ /**
104
+ * if webex metrics is ready, build operational metric backend if not already done.
105
+ */
106
+ private lazyBuildOperationalMetrics() {
107
+ if (this.isReady && !this.operationalMetrics) {
108
+ // @ts-ignore
109
+ this.operationalMetrics = new OperationalMetrics({}, {parent: this.webex});
110
+ }
111
+ }
112
+
113
+ /**
114
+ * if webex metrics is ready, build business metric backend if not already done.
115
+ */
116
+ private lazyBuildBusinessMetrics() {
117
+ if (this.isReady && !this.businessMetrics) {
118
+ // @ts-ignore
119
+ this.businessMetrics = new BusinessMetrics({}, {parent: this.webex});
120
+ }
121
+ }
122
+
89
123
  /**
90
124
  * @returns true once we have the deviceId we need to submit behavioral events to Amplitude
91
125
  */
92
126
  isReadyToSubmitBehavioralEvents() {
93
- return this.behavioralMetrics.isReadyToSubmitBehavioralEvents();
127
+ this.lazyBuildBehavioralMetrics();
128
+
129
+ return this.behavioralMetrics?.isReadyToSubmitEvents() ?? false;
130
+ }
131
+
132
+ /**
133
+ * @returns true once we have the deviceId we need to submit operational events
134
+ */
135
+ isReadyToSubmitOperationalEvents() {
136
+ this.lazyBuildOperationalMetrics();
137
+
138
+ return this.operationalMetrics?.isReadyToSubmitEvents() ?? false;
139
+ }
140
+
141
+ /**
142
+ * @returns true once we have the deviceId we need to submit buisness events
143
+ */
144
+ isReadyToSubmitBusinessEvents() {
145
+ this.lazyBuildBusinessMetrics();
146
+
147
+ return this.businessMetrics?.isReadyToSubmitEvents() ?? false;
94
148
  }
95
149
 
96
150
  /**
@@ -108,9 +162,9 @@ class Metrics extends WebexPlugin {
108
162
  agent: MetricEventAgent;
109
163
  target: string;
110
164
  verb: MetricEventVerb;
111
- payload?: BehavioralEventPayload;
165
+ payload?: EventPayload;
112
166
  }) {
113
- if (!this.behavioralMetrics) {
167
+ if (!this.isReady) {
114
168
  // @ts-ignore
115
169
  this.webex.logger.log(
116
170
  `NewMetrics: @submitBehavioralEvent. Attempted to submit before webex.ready: ${product}.${agent}.${target}.${verb}`
@@ -119,6 +173,8 @@ class Metrics extends WebexPlugin {
119
173
  return Promise.resolve();
120
174
  }
121
175
 
176
+ this.lazyBuildBehavioralMetrics();
177
+
122
178
  return this.behavioralMetrics.submitBehavioralEvent({product, agent, target, verb, payload});
123
179
  }
124
180
 
@@ -126,16 +182,38 @@ class Metrics extends WebexPlugin {
126
182
  * Operational event
127
183
  * @param args
128
184
  */
129
- submitOperationalEvent({
130
- name,
131
- payload,
132
- options,
133
- }: {
134
- name: OperationalEvent['name'];
135
- payload?: RecursivePartial<OperationalEvent['payload']>;
136
- options?: any;
137
- }) {
138
- throw new Error('Not implemented.');
185
+ submitOperationalEvent({name, payload}: {name: string; payload?: EventPayload}) {
186
+ if (!this.isReady) {
187
+ // @ts-ignore
188
+ this.webex.logger.log(
189
+ `NewMetrics: @submitOperationalEvent. Attempted to submit before webex.ready: ${name}`
190
+ );
191
+
192
+ return Promise.resolve();
193
+ }
194
+
195
+ this.lazyBuildOperationalMetrics();
196
+
197
+ return this.operationalMetrics.submitOperationalEvent({name, payload});
198
+ }
199
+
200
+ /**
201
+ * Buisness event
202
+ * @param args
203
+ */
204
+ submitBusinessEvent({name, payload}: {name: string; payload: EventPayload}) {
205
+ if (!this.isReady) {
206
+ // @ts-ignore
207
+ this.webex.logger.log(
208
+ `NewMetrics: @submitBusinessEvent. Attempted to submit before webex.ready: ${name}`
209
+ );
210
+
211
+ return Promise.resolve();
212
+ }
213
+
214
+ this.lazyBuildBusinessMetrics();
215
+
216
+ return this.businessMetrics.submitBusinessEvent({name, payload});
139
217
  }
140
218
 
141
219
  /**
@@ -0,0 +1,24 @@
1
+ import GenericMetrics from './generic-metrics';
2
+ import {EventPayload} from './metrics.types';
3
+
4
+ /**
5
+ * @description Util class to handle Operational Metrics
6
+ * @export
7
+ * @class OperationalMetrics
8
+ */
9
+ export default class OperationalMetrics extends GenericMetrics {
10
+ /**
11
+ * Submit an operational metric to our metrics endpoint.
12
+ * @param {string} name of the metric
13
+ * @param {EventPayload} user payload of the metric
14
+ * @returns {Promise<any>}
15
+ */
16
+ public submitOperationalEvent({name, payload}: {name: string; payload: EventPayload}) {
17
+ const event = this.createTaggedEventObject({
18
+ type: ['operational'],
19
+ name,
20
+ payload,
21
+ });
22
+ this.submitEvent({kind: 'operational-events -> ', name, event});
23
+ }
24
+ }
@@ -74,6 +74,45 @@ describe('internal-plugin-metrics', () => {
74
74
  sinon.restore();
75
75
  });
76
76
 
77
+ describe('#sendEvent', () => {
78
+ it('should send correctly shaped behavioral event (check name building and internal tagged event building)', () => {
79
+ // For some reasons `jest` isn't available when testing form build server - so can't use `jest.fn()` here...
80
+ const requestCalls = [];
81
+ const request = function(arg) { requestCalls.push(arg) }
82
+
83
+ behavioralMetrics.clientMetricsBatcher.request = request;
84
+
85
+ assert.equal(requestCalls.length, 0)
86
+ behavioralMetrics.submitBehavioralEvent({ product: "webex", agent: "user", target: "foo", verb: "get", payload: {bar:"gee"} })
87
+ assert.equal(requestCalls.length, 1)
88
+ assert.deepEqual(requestCalls[0], {
89
+ context: {
90
+ app: {version: 'webex-version'},
91
+ device: {id: 'deviceId'},
92
+ locale: 'language',
93
+ os: {
94
+ name: getOSNameInternal(),
95
+ version: getOSVersion(),
96
+ },
97
+ },
98
+ metricName: 'webex.user.foo.get',
99
+ tags: {
100
+ browser: getBrowserName(),
101
+ browserHeight: window.innerHeight,
102
+ browserVersion: getBrowserVersion(),
103
+ browserWidth: window.innerWidth,
104
+ domain: window.location.hostname,
105
+ inIframe: false,
106
+ locale: window.navigator.language,
107
+ os: getOSNameInternal(),
108
+ bar:"gee"
109
+ },
110
+ timestamp: requestCalls[0].timestamp, // This is to bypass time check, which is correctly tested below.
111
+ type: ['behavioral'],
112
+ });
113
+ })
114
+ })
115
+
77
116
  describe('#getContext', () => {
78
117
  it('should build context correctly', () => {
79
118
  const res = behavioralMetrics.getContext();
@@ -96,7 +135,7 @@ describe('internal-plugin-metrics', () => {
96
135
 
97
136
  describe('#getDefaultTags', () => {
98
137
  it('should build tags correctly', () => {
99
- const res = behavioralMetrics.getDefaultTags();
138
+ const res = behavioralMetrics.getBrowserDetails();
100
139
 
101
140
  assert.deepEqual(res, {
102
141
  browser: getBrowserName(),
@@ -111,25 +150,27 @@ describe('internal-plugin-metrics', () => {
111
150
  });
112
151
  });
113
152
 
114
- describe('#isReadyToSubmitBehavioralEvents', () => {
153
+ describe('#isReadyToSubmitEvents', () => {
115
154
  it('should return true when we have a deviceId, false when deviceId is empty or undefined', async () => {
116
- assert.equal(true, behavioralMetrics.isReadyToSubmitBehavioralEvents());
155
+ let deviceIdUrl = webex.internal.device.url;
117
156
 
157
+ // testing case w/o device id url first, as the internal deviceId cache would bypass that flow.
118
158
  webex.internal.device.url = "";
119
- assert.equal(false, behavioralMetrics.isReadyToSubmitBehavioralEvents());
159
+ assert.equal(false, behavioralMetrics.isReadyToSubmitEvents());
120
160
 
121
161
  delete webex.internal.device.url;
122
- assert.equal(false, behavioralMetrics.isReadyToSubmitBehavioralEvents());
162
+ assert.equal(false, behavioralMetrics.isReadyToSubmitEvents());
163
+
164
+ webex.internal.device.url = deviceIdUrl;
165
+ assert.equal(true, behavioralMetrics.isReadyToSubmitEvents());
123
166
  });
124
167
  });
125
168
 
126
169
  describe('#createEventObject', () => {
127
170
  it('should build event object correctly', async () => {
128
- const res = behavioralMetrics.createEventObject({
129
- product: 'webex',
130
- agent: 'user',
131
- target: 'target',
132
- verb: 'create',
171
+ const res = behavioralMetrics.createTaggedEventObject({
172
+ type:['behavioral'],
173
+ name:'webex.user.target.create',
133
174
  payload: tags,
134
175
  });
135
176
 
@@ -0,0 +1,120 @@
1
+ import sinon from 'sinon';
2
+ import {assert} from '@webex/test-helper-chai';
3
+ import {BrowserDetection} from '@webex/common';
4
+ import {BusinessMetrics, config, getOSNameInternal} from '@webex/internal-plugin-metrics';
5
+ import uuid from 'uuid';
6
+
7
+ //@ts-ignore
8
+ global.window = {location: {hostname: 'whatever'}, navigator: {language: 'language'}};
9
+ process.env.NODE_ENV = 'test';
10
+
11
+ const {getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
12
+
13
+ describe('internal-plugin-metrics', () => {
14
+ describe('BusinessMetrics', () => {
15
+ let webex;
16
+ let now;
17
+ let businessMetrics: BusinessMetrics;
18
+
19
+ const tags = {key: 'val'};
20
+
21
+ beforeEach(() => {
22
+ now = new Date();
23
+
24
+ webex = {
25
+ canAuthorize: true,
26
+ version: 'webex-version',
27
+ internal: {
28
+ services: {
29
+ get: () => 'locus-url',
30
+ },
31
+ metrics: {
32
+ submitClientMetrics: sinon.stub(),
33
+ config: {...config.metrics},
34
+ },
35
+ newMetrics: {},
36
+ device: {
37
+ userId: 'userId',
38
+ url: 'https://wdm-intb.ciscospark.com/wdm/api/v1/devices/deviceId',
39
+ orgId: 'orgId',
40
+ },
41
+ },
42
+ meetings: {
43
+ config: {
44
+ metrics: {
45
+ clientType: 'TEAMS_CLIENT',
46
+ subClientType: 'WEB_APP',
47
+ clientName: 'Cantina',
48
+ },
49
+ },
50
+ geoHintInfo: {
51
+ clientAddress: '1.3.4.5',
52
+ countryCode: 'UK',
53
+ },
54
+ },
55
+ credentials: {
56
+ isUnverifiedGuest: false,
57
+ },
58
+ prepareFetchOptions: sinon.stub().callsFake((opts: any) => ({...opts, foo: 'bar'})),
59
+ request: sinon.stub().resolves({body: {}}),
60
+ logger: {
61
+ log: sinon.stub(),
62
+ error: sinon.stub(),
63
+ },
64
+ };
65
+
66
+ sinon.createSandbox();
67
+ sinon.useFakeTimers(now.getTime());
68
+ businessMetrics = new BusinessMetrics({}, {parent: webex});
69
+ sinon.stub(uuid, 'v4').returns('my-fake-id');
70
+ });
71
+
72
+ afterEach(() => {
73
+ sinon.restore();
74
+ });
75
+
76
+ describe('#sendEvent', () => {
77
+ it('should send correctly shaped business event (check name building and internal tagged event building)', () => {
78
+ // For some reasons `jest` isn't available when testing form build server - so can't use `jest.fn()` here...
79
+ const requestCalls = [];
80
+ const request = function(arg) { requestCalls.push(arg) }
81
+
82
+ businessMetrics.clientMetricsBatcher.request = request;
83
+
84
+ assert.equal(requestCalls.length, 0)
85
+ businessMetrics.submitBusinessEvent({ name: "foobar", payload: {bar:"gee"} })
86
+ assert.equal(requestCalls.length, 1)
87
+ assert.deepEqual(requestCalls[0], {
88
+ eventPayload: {
89
+ context: {
90
+ app: {version: 'webex-version'},
91
+ device: {id: 'deviceId'},
92
+ locale: 'language',
93
+ os: {
94
+ name: getOSNameInternal(),
95
+ version: getOSVersion(),
96
+ },
97
+ },
98
+ key: 'foobar',
99
+ browserDetails: {
100
+ browser: getBrowserName(),
101
+ browserHeight: window.innerHeight,
102
+ browserVersion: getBrowserVersion(),
103
+ browserWidth: window.innerWidth,
104
+ domain: window.location.hostname,
105
+ inIframe: false,
106
+ locale: window.navigator.language,
107
+ os: getOSNameInternal(),
108
+ },
109
+ client_timestamp: requestCalls[0].eventPayload.client_timestamp, // This is to bypass time check, which is checked below.
110
+ value: {
111
+ bar: "gee"
112
+ }
113
+ },
114
+ type: ['business'],
115
+ });
116
+ assert.isNumber(requestCalls[0].eventPayload.client_timestamp)
117
+ })
118
+ })
119
+ });
120
+ });
@@ -441,7 +441,7 @@ describe('plugin-metrics', () => {
441
441
  // item also gets assigned a delay property but the key is a Symbol and haven't been able to test that..
442
442
  assert.deepEqual(calls.args[0].eventPayload, {
443
443
  event: 'my.event',
444
- origin: {buildType: 'test', networkType: 'unknown'},
444
+ origin: {buildType: 'test', networkType: 'unknown', upgradeChannel: 'test'},
445
445
  });
446
446
 
447
447
  assert.deepEqual(calls.args[0].type, ['diagnostic-event']);
@@ -455,6 +455,7 @@ describe('plugin-metrics', () => {
455
455
  origin: {
456
456
  buildType: 'test',
457
457
  networkType: 'unknown',
458
+ upgradeChannel: 'test',
458
459
  },
459
460
  });
460
461
  assert.deepEqual(prepareDiagnosticMetricItemCalls[0].args[1].type, ['diagnostic-event']);