@webex/internal-plugin-metrics 3.5.0-next.3 → 3.5.0-next.5

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.
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_uuid","_interopRequireDefault","require","CallDiagnosticUtils","_interopRequireWildcard","_constants","_getRequireWildcardCache","e","_WeakMap","r","t","__esModule","_typeof","default","has","get","n","__proto__","a","_Object$defineProperty","_Object$getOwnPropertyDescriptor","u","Object","prototype","hasOwnProperty","call","i","set","parseJsonPayload","payload","JSON","parse","_","RtcMetrics","exports","webex","meetingId","correlationId","_classCallCheck2","_defineProperty2","intervalId","window","setInterval","sendMetricsInQueue","bind","resetConnection","_createClass2","key","value","metricsQueue","length","sendMetrics","sendNextMetrics","shouldSendMetricsOnNextStatsReport","addMetrics","data","name","map","anonymizeIp","push","parsedPayload","console","error","closeMetrics","clearInterval","stats","type","ip","anonymizeIPAddress","undefined","address","relatedAddress","_stringify","connectionId","uuid","v4","request","method","service","resource","headers","appId","RTC_METRICS","APP_ID","body","metrics","version","userId","internal","device"],"sources":["index.ts"],"sourcesContent":["/* eslint-disable class-methods-use-this */\nimport uuid from 'uuid';\nimport * as CallDiagnosticUtils from '../call-diagnostic/call-diagnostic-metrics.util';\nimport RTC_METRICS from './constants';\n\nconst parseJsonPayload = (payload: any[]): any | null => {\n try {\n if (payload && payload[0]) {\n return JSON.parse(payload[0]);\n }\n\n return null;\n } catch (_) {\n return null;\n }\n};\n\n/**\n * Rtc Metrics\n */\nexport default class RtcMetrics {\n /**\n * Array of MetricData items to be sent to the metrics service.\n */\n metricsQueue = [];\n\n intervalId: number;\n\n webex: any;\n\n meetingId: string;\n\n correlationId: string;\n\n connectionId: string;\n\n shouldSendMetricsOnNextStatsReport: boolean;\n\n /**\n * Initialize the interval.\n *\n * @param {object} webex - The main `webex` object.\n * @param {string} meetingId - The meeting id.\n * @param {string} correlationId - The correlation id.\n */\n constructor(webex, meetingId, correlationId) {\n // `window` is used to prevent typescript from returning a NodeJS.Timer.\n this.intervalId = window.setInterval(this.sendMetricsInQueue.bind(this), 30 * 1000);\n this.meetingId = meetingId;\n this.webex = webex;\n this.correlationId = correlationId;\n this.resetConnection();\n }\n\n /**\n * Check to see if the metrics queue has any items.\n *\n * @returns {void}\n */\n public sendMetricsInQueue() {\n if (this.metricsQueue.length) {\n this.sendMetrics();\n this.metricsQueue = [];\n }\n }\n\n /**\n * Forces sending metrics when we get the next stats-report\n *\n * This is useful for cases when something important happens that affects the media connection,\n * for example when we move from lobby into the meeting.\n *\n * @returns {void}\n */\n public sendNextMetrics() {\n this.shouldSendMetricsOnNextStatsReport = true;\n }\n\n /**\n * Add metrics items to the metrics queue.\n *\n * @param {object} data - An object with a payload array of metrics items.\n *\n * @returns {void}\n */\n addMetrics(data) {\n if (data.payload.length) {\n if (data.name === 'stats-report') {\n data.payload = data.payload.map(this.anonymizeIp);\n }\n\n this.metricsQueue.push(data);\n\n if (this.shouldSendMetricsOnNextStatsReport && data.name === 'stats-report') {\n // this is the first useful set of data (WCME gives it to us after 5s), send it out immediately\n // in case the user is unhappy and closes the browser early\n this.sendMetricsInQueue();\n this.shouldSendMetricsOnNextStatsReport = false;\n }\n\n try {\n // If a connection fails, send the rest of the metrics in queue and get a new connection id.\n const parsedPayload = parseJsonPayload(data.payload);\n if (\n data.name === 'onconnectionstatechange' &&\n parsedPayload &&\n parsedPayload.value === 'failed'\n ) {\n this.sendMetricsInQueue();\n this.resetConnection();\n }\n } catch (e) {\n console.error(e);\n }\n }\n }\n\n /**\n * Clear the metrics interval.\n *\n * @returns {void}\n */\n closeMetrics() {\n this.sendMetricsInQueue();\n clearInterval(this.intervalId);\n }\n\n /**\n * Anonymize IP addresses.\n *\n * @param {array} stats - An RTCStatsReport organized into an array of strings.\n * @returns {string}\n */\n anonymizeIp(stats: string): string {\n const data = JSON.parse(stats);\n // on local and remote candidates, anonymize the last 4 bits.\n if (data.type === 'local-candidate' || data.type === 'remote-candidate') {\n data.ip = CallDiagnosticUtils.anonymizeIPAddress(data.ip) || undefined;\n data.address = CallDiagnosticUtils.anonymizeIPAddress(data.address) || undefined;\n data.relatedAddress =\n CallDiagnosticUtils.anonymizeIPAddress(data.relatedAddress) || undefined;\n }\n\n return JSON.stringify(data);\n }\n\n /**\n * Set a new connection id.\n *\n * @returns {void}\n */\n private resetConnection() {\n this.connectionId = uuid.v4();\n this.shouldSendMetricsOnNextStatsReport = true;\n }\n\n /**\n * Send metrics to the metrics service.\n *\n * @returns {void}\n */\n private sendMetrics() {\n this.webex.request({\n method: 'POST',\n service: 'unifiedTelemetry',\n resource: 'metric/v2',\n headers: {\n type: 'webrtcMedia',\n appId: RTC_METRICS.APP_ID,\n },\n body: {\n metrics: [\n {\n type: 'webrtc',\n version: '1.1.0',\n userId: this.webex.internal.device.userId,\n meetingId: this.meetingId,\n correlationId: this.correlationId,\n connectionId: this.connectionId,\n data: this.metricsQueue,\n },\n ],\n },\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AACA,IAAAA,KAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAC,uBAAA,CAAAF,OAAA;AACA,IAAAG,UAAA,GAAAJ,sBAAA,CAAAC,OAAA;AAAsC,SAAAI,yBAAAC,CAAA,6BAAAC,QAAA,mBAAAC,CAAA,OAAAD,QAAA,IAAAE,CAAA,OAAAF,QAAA,YAAAF,wBAAA,YAAAA,yBAAAC,CAAA,WAAAA,CAAA,GAAAG,CAAA,GAAAD,CAAA,KAAAF,CAAA;AAAA,SAAAH,wBAAAG,CAAA,EAAAE,CAAA,SAAAA,CAAA,IAAAF,CAAA,IAAAA,CAAA,CAAAI,UAAA,SAAAJ,CAAA,eAAAA,CAAA,gBAAAK,OAAA,CAAAL,CAAA,0BAAAA,CAAA,WAAAM,OAAA,EAAAN,CAAA,QAAAG,CAAA,GAAAJ,wBAAA,CAAAG,CAAA,OAAAC,CAAA,IAAAA,CAAA,CAAAI,GAAA,CAAAP,CAAA,UAAAG,CAAA,CAAAK,GAAA,CAAAR,CAAA,OAAAS,CAAA,KAAAC,SAAA,UAAAC,CAAA,GAAAC,sBAAA,IAAAC,gCAAA,WAAAC,CAAA,IAAAd,CAAA,oBAAAc,CAAA,IAAAC,MAAA,CAAAC,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAlB,CAAA,EAAAc,CAAA,SAAAK,CAAA,GAAAR,CAAA,GAAAE,gCAAA,CAAAb,CAAA,EAAAc,CAAA,UAAAK,CAAA,KAAAA,CAAA,CAAAX,GAAA,IAAAW,CAAA,CAAAC,GAAA,IAAAR,sBAAA,CAAAH,CAAA,EAAAK,CAAA,EAAAK,CAAA,IAAAV,CAAA,CAAAK,CAAA,IAAAd,CAAA,CAAAc,CAAA,YAAAL,CAAA,CAAAH,OAAA,GAAAN,CAAA,EAAAG,CAAA,IAAAA,CAAA,CAAAiB,GAAA,CAAApB,CAAA,EAAAS,CAAA,GAAAA,CAAA;AAHtC;;AAKA,IAAMY,gBAAgB,GAAG,SAAnBA,gBAAgBA,CAAIC,OAAc,EAAiB;EACvD,IAAI;IACF,IAAIA,OAAO,IAAIA,OAAO,CAAC,CAAC,CAAC,EAAE;MACzB,OAAOC,IAAI,CAACC,KAAK,CAACF,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/B;IAEA,OAAO,IAAI;EACb,CAAC,CAAC,OAAOG,CAAC,EAAE;IACV,OAAO,IAAI;EACb;AACF,CAAC;;AAED;AACA;AACA;AAFA,IAGqBC,UAAU,GAAAC,OAAA,CAAArB,OAAA;EAkB7B;AACF;AACA;AACA;AACA;AACA;AACA;EACE,SAAAoB,WAAYE,KAAK,EAAEC,SAAS,EAAEC,aAAa,EAAE;IAAA,IAAAC,gBAAA,CAAAzB,OAAA,QAAAoB,UAAA;IAxB7C;AACF;AACA;IAFE,IAAAM,gBAAA,CAAA1B,OAAA,wBAGe,EAAE;IAAA,IAAA0B,gBAAA,CAAA1B,OAAA;IAAA,IAAA0B,gBAAA,CAAA1B,OAAA;IAAA,IAAA0B,gBAAA,CAAA1B,OAAA;IAAA,IAAA0B,gBAAA,CAAA1B,OAAA;IAAA,IAAA0B,gBAAA,CAAA1B,OAAA;IAAA,IAAA0B,gBAAA,CAAA1B,OAAA;IAsBf;IACA,IAAI,CAAC2B,UAAU,GAAGC,MAAM,CAACC,WAAW,CAAC,IAAI,CAACC,kBAAkB,CAACC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC;IACnF,IAAI,CAACR,SAAS,GAAGA,SAAS;IAC1B,IAAI,CAACD,KAAK,GAAGA,KAAK;IAClB,IAAI,CAACE,aAAa,GAAGA,aAAa;IAClC,IAAI,CAACQ,eAAe,CAAC,CAAC;EACxB;;EAEA;AACF;AACA;AACA;AACA;EAJE,IAAAC,aAAA,CAAAjC,OAAA,EAAAoB,UAAA;IAAAc,GAAA;IAAAC,KAAA,EAKA,SAAAL,mBAAA,EAA4B;MAC1B,IAAI,IAAI,CAACM,YAAY,CAACC,MAAM,EAAE;QAC5B,IAAI,CAACC,WAAW,CAAC,CAAC;QAClB,IAAI,CAACF,YAAY,GAAG,EAAE;MACxB;IACF;;IAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EAPE;IAAAF,GAAA;IAAAC,KAAA,EAQA,SAAAI,gBAAA,EAAyB;MACvB,IAAI,CAACC,kCAAkC,GAAG,IAAI;IAChD;;IAEA;AACF;AACA;AACA;AACA;AACA;AACA;EANE;IAAAN,GAAA;IAAAC,KAAA,EAOA,SAAAM,WAAWC,IAAI,EAAE;MACf,IAAIA,IAAI,CAAC1B,OAAO,CAACqB,MAAM,EAAE;QACvB,IAAIK,IAAI,CAACC,IAAI,KAAK,cAAc,EAAE;UAChCD,IAAI,CAAC1B,OAAO,GAAG0B,IAAI,CAAC1B,OAAO,CAAC4B,GAAG,CAAC,IAAI,CAACC,WAAW,CAAC;QACnD;QAEA,IAAI,CAACT,YAAY,CAACU,IAAI,CAACJ,IAAI,CAAC;QAE5B,IAAI,IAAI,CAACF,kCAAkC,IAAIE,IAAI,CAACC,IAAI,KAAK,cAAc,EAAE;UAC3E;UACA;UACA,IAAI,CAACb,kBAAkB,CAAC,CAAC;UACzB,IAAI,CAACU,kCAAkC,GAAG,KAAK;QACjD;QAEA,IAAI;UACF;UACA,IAAMO,aAAa,GAAGhC,gBAAgB,CAAC2B,IAAI,CAAC1B,OAAO,CAAC;UACpD,IACE0B,IAAI,CAACC,IAAI,KAAK,yBAAyB,IACvCI,aAAa,IACbA,aAAa,CAACZ,KAAK,KAAK,QAAQ,EAChC;YACA,IAAI,CAACL,kBAAkB,CAAC,CAAC;YACzB,IAAI,CAACE,eAAe,CAAC,CAAC;UACxB;QACF,CAAC,CAAC,OAAOtC,CAAC,EAAE;UACVsD,OAAO,CAACC,KAAK,CAACvD,CAAC,CAAC;QAClB;MACF;IACF;;IAEA;AACF;AACA;AACA;AACA;EAJE;IAAAwC,GAAA;IAAAC,KAAA,EAKA,SAAAe,aAAA,EAAe;MACb,IAAI,CAACpB,kBAAkB,CAAC,CAAC;MACzBqB,aAAa,CAAC,IAAI,CAACxB,UAAU,CAAC;IAChC;;IAEA;AACF;AACA;AACA;AACA;AACA;EALE;IAAAO,GAAA;IAAAC,KAAA,EAMA,SAAAU,YAAYO,KAAa,EAAU;MACjC,IAAMV,IAAI,GAAGzB,IAAI,CAACC,KAAK,CAACkC,KAAK,CAAC;MAC9B;MACA,IAAIV,IAAI,CAACW,IAAI,KAAK,iBAAiB,IAAIX,IAAI,CAACW,IAAI,KAAK,kBAAkB,EAAE;QACvEX,IAAI,CAACY,EAAE,GAAGhE,mBAAmB,CAACiE,kBAAkB,CAACb,IAAI,CAACY,EAAE,CAAC,IAAIE,SAAS;QACtEd,IAAI,CAACe,OAAO,GAAGnE,mBAAmB,CAACiE,kBAAkB,CAACb,IAAI,CAACe,OAAO,CAAC,IAAID,SAAS;QAChFd,IAAI,CAACgB,cAAc,GACjBpE,mBAAmB,CAACiE,kBAAkB,CAACb,IAAI,CAACgB,cAAc,CAAC,IAAIF,SAAS;MAC5E;MAEA,OAAO,IAAAG,UAAA,CAAA3D,OAAA,EAAe0C,IAAI,CAAC;IAC7B;;IAEA;AACF;AACA;AACA;AACA;EAJE;IAAAR,GAAA;IAAAC,KAAA,EAKA,SAAAH,gBAAA,EAA0B;MACxB,IAAI,CAAC4B,YAAY,GAAGC,aAAI,CAACC,EAAE,CAAC,CAAC;MAC7B,IAAI,CAACtB,kCAAkC,GAAG,IAAI;IAChD;;IAEA;AACF;AACA;AACA;AACA;EAJE;IAAAN,GAAA;IAAAC,KAAA,EAKA,SAAAG,YAAA,EAAsB;MACpB,IAAI,CAAChB,KAAK,CAACyC,OAAO,CAAC;QACjBC,MAAM,EAAE,MAAM;QACdC,OAAO,EAAE,kBAAkB;QAC3BC,QAAQ,EAAE,WAAW;QACrBC,OAAO,EAAE;UACPd,IAAI,EAAE,aAAa;UACnBe,KAAK,EAAEC,kBAAW,CAACC;QACrB,CAAC;QACDC,IAAI,EAAE;UACJC,OAAO,EAAE,CACP;YACEnB,IAAI,EAAE,QAAQ;YACdoB,OAAO,EAAE,OAAO;YAChBC,MAAM,EAAE,IAAI,CAACpD,KAAK,CAACqD,QAAQ,CAACC,MAAM,CAACF,MAAM;YACzCnD,SAAS,EAAE,IAAI,CAACA,SAAS;YACzBC,aAAa,EAAE,IAAI,CAACA,aAAa;YACjCoC,YAAY,EAAE,IAAI,CAACA,YAAY;YAC/BlB,IAAI,EAAE,IAAI,CAACN;UACb,CAAC;QAEL;MACF,CAAC,CAAC;IACJ;EAAC;EAAA,OAAAhB,UAAA;AAAA"}
@@ -12,6 +12,7 @@ import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-l
12
12
  import BehavioralMetrics from './behavioral-metrics';
13
13
  import OperationalMetrics from './operational-metrics';
14
14
  import BusinessMetrics from './business-metrics';
15
+ import RtcMetrics from './rtcMetrics';
15
16
  export { default, getOSNameInternal } from './metrics';
16
- export { config, CALL_DIAGNOSTIC_CONFIG, NewMetrics, Utils, CallDiagnosticUtils, CallDiagnosticLatencies, CallDiagnosticMetrics, BehavioralMetrics, OperationalMetrics, BusinessMetrics, };
17
+ export { config, CALL_DIAGNOSTIC_CONFIG, NewMetrics, Utils, CallDiagnosticUtils, CallDiagnosticLatencies, CallDiagnosticMetrics, BehavioralMetrics, OperationalMetrics, BusinessMetrics, RtcMetrics, };
17
18
  export type { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitMQE, SubmitOperationalEvent, PreComputedLatencies, };
@@ -0,0 +1,4 @@
1
+ declare const RTC_METRICS: {
2
+ APP_ID: string;
3
+ };
4
+ export { RTC_METRICS as default };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Rtc Metrics
3
+ */
4
+ export default class RtcMetrics {
5
+ /**
6
+ * Array of MetricData items to be sent to the metrics service.
7
+ */
8
+ metricsQueue: any[];
9
+ intervalId: number;
10
+ webex: any;
11
+ meetingId: string;
12
+ correlationId: string;
13
+ connectionId: string;
14
+ shouldSendMetricsOnNextStatsReport: boolean;
15
+ /**
16
+ * Initialize the interval.
17
+ *
18
+ * @param {object} webex - The main `webex` object.
19
+ * @param {string} meetingId - The meeting id.
20
+ * @param {string} correlationId - The correlation id.
21
+ */
22
+ constructor(webex: any, meetingId: any, correlationId: any);
23
+ /**
24
+ * Check to see if the metrics queue has any items.
25
+ *
26
+ * @returns {void}
27
+ */
28
+ sendMetricsInQueue(): void;
29
+ /**
30
+ * Forces sending metrics when we get the next stats-report
31
+ *
32
+ * This is useful for cases when something important happens that affects the media connection,
33
+ * for example when we move from lobby into the meeting.
34
+ *
35
+ * @returns {void}
36
+ */
37
+ sendNextMetrics(): void;
38
+ /**
39
+ * Add metrics items to the metrics queue.
40
+ *
41
+ * @param {object} data - An object with a payload array of metrics items.
42
+ *
43
+ * @returns {void}
44
+ */
45
+ addMetrics(data: any): void;
46
+ /**
47
+ * Clear the metrics interval.
48
+ *
49
+ * @returns {void}
50
+ */
51
+ closeMetrics(): void;
52
+ /**
53
+ * Anonymize IP addresses.
54
+ *
55
+ * @param {array} stats - An RTCStatsReport organized into an array of strings.
56
+ * @returns {string}
57
+ */
58
+ anonymizeIp(stats: string): string;
59
+ /**
60
+ * Set a new connection id.
61
+ *
62
+ * @returns {void}
63
+ */
64
+ private resetConnection;
65
+ /**
66
+ * Send metrics to the metrics service.
67
+ *
68
+ * @returns {void}
69
+ */
70
+ private sendMetrics;
71
+ }
package/package.json CHANGED
@@ -26,22 +26,22 @@
26
26
  "@webex/eslint-config-legacy": "0.0.0",
27
27
  "@webex/jest-config-legacy": "0.0.0",
28
28
  "@webex/legacy-tools": "0.0.0",
29
- "@webex/test-helper-chai": "3.5.0-next.3",
30
- "@webex/test-helper-mocha": "3.5.0-next.3",
31
- "@webex/test-helper-mock-webex": "3.5.0-next.3",
32
- "@webex/test-helper-test-users": "3.5.0-next.3",
29
+ "@webex/test-helper-chai": "3.5.0-next.5",
30
+ "@webex/test-helper-mocha": "3.5.0-next.5",
31
+ "@webex/test-helper-mock-webex": "3.5.0-next.5",
32
+ "@webex/test-helper-test-users": "3.5.0-next.5",
33
33
  "eslint": "^8.24.0",
34
34
  "prettier": "^2.7.1",
35
35
  "sinon": "^9.2.4"
36
36
  },
37
37
  "dependencies": {
38
- "@webex/common": "3.5.0-next.3",
39
- "@webex/common-timers": "3.5.0-next.3",
38
+ "@webex/common": "3.5.0-next.5",
39
+ "@webex/common-timers": "3.5.0-next.5",
40
40
  "@webex/event-dictionary-ts": "^1.0.1546",
41
- "@webex/internal-plugin-metrics": "3.5.0-next.3",
42
- "@webex/test-helper-chai": "3.5.0-next.3",
43
- "@webex/test-helper-mock-webex": "3.5.0-next.3",
44
- "@webex/webex-core": "3.5.0-next.3",
41
+ "@webex/internal-plugin-metrics": "3.5.0-next.5",
42
+ "@webex/test-helper-chai": "3.5.0-next.5",
43
+ "@webex/test-helper-mock-webex": "3.5.0-next.5",
44
+ "@webex/webex-core": "3.5.0-next.5",
45
45
  "ip-anonymize": "^0.1.0",
46
46
  "lodash": "^4.17.21",
47
47
  "uuid": "^3.3.2"
@@ -54,5 +54,5 @@
54
54
  "test:style": "eslint ./src/**/*.*",
55
55
  "test:unit": "webex-legacy-tools test --unit --runner mocha"
56
56
  },
57
- "version": "3.5.0-next.3"
57
+ "version": "3.5.0-next.5"
58
58
  }
@@ -289,13 +289,14 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
289
289
  sessionCorrelationId,
290
290
  } = options;
291
291
  const identifiers: Event['event']['identifiers'] = {
292
- correlationId: 'unknown',
293
- sessionCorrelationId: 'unknown',
292
+ correlationId: 'unknown', // concerned with setting this to unknown. This will fail diagnostic events parsing because it's not a uuid pattern
294
293
  };
295
294
 
296
295
  if (meeting) {
297
296
  identifiers.correlationId = meeting.correlationId;
298
- identifiers.sessionCorrelationId = meeting.sessionCorrelationId;
297
+ if (meeting.sessionCorrelationId) {
298
+ identifiers.sessionCorrelationId = meeting.sessionCorrelationId;
299
+ }
299
300
  }
300
301
 
301
302
  if (sessionCorrelationId) {
@@ -306,6 +307,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
306
307
  identifiers.correlationId = correlationId;
307
308
  }
308
309
 
310
+ // TODO: should we use patterns.uuid to validate correlationId and session correlation id? they will fail the diagnostic events validation pipeline if improperly formatted
311
+
309
312
  if (this.device) {
310
313
  const {device} = this;
311
314
  const {installationId} = device?.config || {};
package/src/index.ts CHANGED
@@ -25,6 +25,7 @@ import CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-l
25
25
  import BehavioralMetrics from './behavioral-metrics';
26
26
  import OperationalMetrics from './operational-metrics';
27
27
  import BusinessMetrics from './business-metrics';
28
+ import RtcMetrics from './rtcMetrics';
28
29
 
29
30
  registerInternalPlugin('metrics', Metrics, {
30
31
  config,
@@ -47,6 +48,7 @@ export {
47
48
  BehavioralMetrics,
48
49
  OperationalMetrics,
49
50
  BusinessMetrics,
51
+ RtcMetrics,
50
52
  };
51
53
  export type {
52
54
  ClientEvent,
@@ -0,0 +1,3 @@
1
+ const RTC_METRICS = {APP_ID: 'FFB51ED5-4319-4C55-8303-B1F2FCCDE231'};
2
+
3
+ export {RTC_METRICS as default};
@@ -0,0 +1,186 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ import uuid from 'uuid';
3
+ import * as CallDiagnosticUtils from '../call-diagnostic/call-diagnostic-metrics.util';
4
+ import RTC_METRICS from './constants';
5
+
6
+ const parseJsonPayload = (payload: any[]): any | null => {
7
+ try {
8
+ if (payload && payload[0]) {
9
+ return JSON.parse(payload[0]);
10
+ }
11
+
12
+ return null;
13
+ } catch (_) {
14
+ return null;
15
+ }
16
+ };
17
+
18
+ /**
19
+ * Rtc Metrics
20
+ */
21
+ export default class RtcMetrics {
22
+ /**
23
+ * Array of MetricData items to be sent to the metrics service.
24
+ */
25
+ metricsQueue = [];
26
+
27
+ intervalId: number;
28
+
29
+ webex: any;
30
+
31
+ meetingId: string;
32
+
33
+ correlationId: string;
34
+
35
+ connectionId: string;
36
+
37
+ shouldSendMetricsOnNextStatsReport: boolean;
38
+
39
+ /**
40
+ * Initialize the interval.
41
+ *
42
+ * @param {object} webex - The main `webex` object.
43
+ * @param {string} meetingId - The meeting id.
44
+ * @param {string} correlationId - The correlation id.
45
+ */
46
+ constructor(webex, meetingId, correlationId) {
47
+ // `window` is used to prevent typescript from returning a NodeJS.Timer.
48
+ this.intervalId = window.setInterval(this.sendMetricsInQueue.bind(this), 30 * 1000);
49
+ this.meetingId = meetingId;
50
+ this.webex = webex;
51
+ this.correlationId = correlationId;
52
+ this.resetConnection();
53
+ }
54
+
55
+ /**
56
+ * Check to see if the metrics queue has any items.
57
+ *
58
+ * @returns {void}
59
+ */
60
+ public sendMetricsInQueue() {
61
+ if (this.metricsQueue.length) {
62
+ this.sendMetrics();
63
+ this.metricsQueue = [];
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Forces sending metrics when we get the next stats-report
69
+ *
70
+ * This is useful for cases when something important happens that affects the media connection,
71
+ * for example when we move from lobby into the meeting.
72
+ *
73
+ * @returns {void}
74
+ */
75
+ public sendNextMetrics() {
76
+ this.shouldSendMetricsOnNextStatsReport = true;
77
+ }
78
+
79
+ /**
80
+ * Add metrics items to the metrics queue.
81
+ *
82
+ * @param {object} data - An object with a payload array of metrics items.
83
+ *
84
+ * @returns {void}
85
+ */
86
+ addMetrics(data) {
87
+ if (data.payload.length) {
88
+ if (data.name === 'stats-report') {
89
+ data.payload = data.payload.map(this.anonymizeIp);
90
+ }
91
+
92
+ this.metricsQueue.push(data);
93
+
94
+ if (this.shouldSendMetricsOnNextStatsReport && data.name === 'stats-report') {
95
+ // this is the first useful set of data (WCME gives it to us after 5s), send it out immediately
96
+ // in case the user is unhappy and closes the browser early
97
+ this.sendMetricsInQueue();
98
+ this.shouldSendMetricsOnNextStatsReport = false;
99
+ }
100
+
101
+ try {
102
+ // If a connection fails, send the rest of the metrics in queue and get a new connection id.
103
+ const parsedPayload = parseJsonPayload(data.payload);
104
+ if (
105
+ data.name === 'onconnectionstatechange' &&
106
+ parsedPayload &&
107
+ parsedPayload.value === 'failed'
108
+ ) {
109
+ this.sendMetricsInQueue();
110
+ this.resetConnection();
111
+ }
112
+ } catch (e) {
113
+ console.error(e);
114
+ }
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Clear the metrics interval.
120
+ *
121
+ * @returns {void}
122
+ */
123
+ closeMetrics() {
124
+ this.sendMetricsInQueue();
125
+ clearInterval(this.intervalId);
126
+ }
127
+
128
+ /**
129
+ * Anonymize IP addresses.
130
+ *
131
+ * @param {array} stats - An RTCStatsReport organized into an array of strings.
132
+ * @returns {string}
133
+ */
134
+ anonymizeIp(stats: string): string {
135
+ const data = JSON.parse(stats);
136
+ // on local and remote candidates, anonymize the last 4 bits.
137
+ if (data.type === 'local-candidate' || data.type === 'remote-candidate') {
138
+ data.ip = CallDiagnosticUtils.anonymizeIPAddress(data.ip) || undefined;
139
+ data.address = CallDiagnosticUtils.anonymizeIPAddress(data.address) || undefined;
140
+ data.relatedAddress =
141
+ CallDiagnosticUtils.anonymizeIPAddress(data.relatedAddress) || undefined;
142
+ }
143
+
144
+ return JSON.stringify(data);
145
+ }
146
+
147
+ /**
148
+ * Set a new connection id.
149
+ *
150
+ * @returns {void}
151
+ */
152
+ private resetConnection() {
153
+ this.connectionId = uuid.v4();
154
+ this.shouldSendMetricsOnNextStatsReport = true;
155
+ }
156
+
157
+ /**
158
+ * Send metrics to the metrics service.
159
+ *
160
+ * @returns {void}
161
+ */
162
+ private sendMetrics() {
163
+ this.webex.request({
164
+ method: 'POST',
165
+ service: 'unifiedTelemetry',
166
+ resource: 'metric/v2',
167
+ headers: {
168
+ type: 'webrtcMedia',
169
+ appId: RTC_METRICS.APP_ID,
170
+ },
171
+ body: {
172
+ metrics: [
173
+ {
174
+ type: 'webrtc',
175
+ version: '1.1.0',
176
+ userId: this.webex.internal.device.userId,
177
+ meetingId: this.meetingId,
178
+ correlationId: this.correlationId,
179
+ connectionId: this.connectionId,
180
+ data: this.metricsQueue,
181
+ },
182
+ ],
183
+ },
184
+ });
185
+ }
186
+ }