@nordicsemiconductor/pc-nrfconnect-shared 150.0.0 → 152.0.0

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 (56) hide show
  1. package/Changelog.md +31 -1
  2. package/nrfutil/sandbox.ts +6 -6
  3. package/package.json +4 -4
  4. package/src/App/App.test.tsx +11 -2
  5. package/src/App/App.tsx +3 -3
  6. package/src/Device/DeviceSelector/DeviceSelector.test.tsx +13 -0
  7. package/src/Device/DeviceSelector/DeviceSelector.tsx +6 -6
  8. package/src/Device/deviceLister.ts +4 -4
  9. package/src/ErrorBoundary/ErrorBoundary.test.tsx +5 -5
  10. package/src/ErrorBoundary/ErrorBoundary.tsx +14 -13
  11. package/src/Parsers/shellParser.ts +3 -3
  12. package/src/index.ts +2 -2
  13. package/src/telemetry/TelemetryMetadata.ts +9 -0
  14. package/src/telemetry/TelemetrySender.ts +80 -0
  15. package/src/telemetry/TelemetrySenderInMain.ts +80 -0
  16. package/src/telemetry/TelemetrySenderInRenderer.ts +86 -0
  17. package/src/telemetry/flatObject.test.ts +37 -0
  18. package/src/telemetry/flatObject.ts +19 -0
  19. package/src/telemetry/simplifyDevice.ts +20 -0
  20. package/src/telemetry/telemetry.ts +97 -0
  21. package/src/utils/persistentStore.ts +7 -7
  22. package/src/utils/systemReport.ts +1 -1
  23. package/src/utils/useHotKey.ts +2 -2
  24. package/typings/generated/src/ErrorBoundary/ErrorBoundary.d.ts +1 -1
  25. package/typings/generated/src/ErrorBoundary/ErrorBoundary.d.ts.map +1 -1
  26. package/typings/generated/src/index.d.ts +2 -2
  27. package/typings/generated/src/index.d.ts.map +1 -1
  28. package/typings/generated/src/telemetry/TelemetryMetadata.d.ts +4 -0
  29. package/typings/generated/src/telemetry/TelemetryMetadata.d.ts.map +1 -0
  30. package/typings/generated/src/telemetry/TelemetrySender.d.ts +24 -0
  31. package/typings/generated/src/telemetry/TelemetrySender.d.ts.map +1 -0
  32. package/typings/generated/src/telemetry/TelemetrySenderInMain.d.ts +15 -0
  33. package/typings/generated/src/telemetry/TelemetrySenderInMain.d.ts.map +1 -0
  34. package/typings/generated/src/telemetry/TelemetrySenderInRenderer.d.ts +15 -0
  35. package/typings/generated/src/telemetry/TelemetrySenderInRenderer.d.ts.map +1 -0
  36. package/typings/generated/src/telemetry/flatObject.d.ts +4 -0
  37. package/typings/generated/src/telemetry/flatObject.d.ts.map +1 -0
  38. package/typings/generated/src/telemetry/flatObject.test.d.ts +2 -0
  39. package/typings/generated/src/telemetry/flatObject.test.d.ts.map +1 -0
  40. package/typings/generated/src/{utils/usageDataCommon.d.ts → telemetry/simplifyDevice.d.ts} +2 -14
  41. package/typings/generated/src/telemetry/simplifyDevice.d.ts.map +1 -0
  42. package/typings/generated/src/telemetry/telemetry.d.ts +16 -0
  43. package/typings/generated/src/telemetry/telemetry.d.ts.map +1 -0
  44. package/typings/generated/src/utils/persistentStore.d.ts +4 -4
  45. package/typings/generated/src/utils/persistentStore.d.ts.map +1 -1
  46. package/src/utils/usageData.ts +0 -117
  47. package/src/utils/usageDataCommon.ts +0 -59
  48. package/src/utils/usageDataMain.ts +0 -119
  49. package/src/utils/usageDataRenderer.ts +0 -137
  50. package/typings/generated/src/utils/usageData.d.ts +0 -16
  51. package/typings/generated/src/utils/usageData.d.ts.map +0 -1
  52. package/typings/generated/src/utils/usageDataCommon.d.ts.map +0 -1
  53. package/typings/generated/src/utils/usageDataMain.d.ts +0 -10
  54. package/typings/generated/src/utils/usageDataMain.d.ts.map +0 -1
  55. package/typings/generated/src/utils/usageDataRenderer.d.ts +0 -10
  56. package/typings/generated/src/utils/usageDataRenderer.d.ts.map +0 -1
package/Changelog.md CHANGED
@@ -7,6 +7,36 @@ This project does _not_ adhere to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html) but contrary to it
8
8
  every new version is a new major version.
9
9
 
10
+ ## 152.0.0 - 2024-01-26
11
+
12
+ ### Updated
13
+
14
+ - Electron version to `28.1.4`
15
+ - @electron/remote to `2.1.1`
16
+
17
+ ## 151.0.0 - 2024-01-23
18
+
19
+ ### Changed
20
+
21
+ - Renamed exported object `usageData` to `telemetry` and type
22
+ `UsageDataMetadata` to `TelemetryMetadata`.
23
+ - Renamed several function in the `telemetry` object:
24
+ - `enable()` → `setUsersAgreedToTelemetry(true)`
25
+ - `disable()` → `setUsersAgreedToTelemetry(false)`
26
+ - `reset()` → `setUsersWithdrewTelemetryAgreement()`
27
+ - `isEnabled()` → `getIsSendingTelemetry()` (which now does not log
28
+ anymore)
29
+ - `sendUsageData()` → `sendEvent()`
30
+ - In the component `ErrorBoundary` the property `sendUsageData` is renamed to
31
+ `sendTelemetryEvent`.
32
+
33
+ ### Steps to upgrade when using this package
34
+
35
+ - If they are imported from shared, rename `usageData` and `UsageDataMetadata`
36
+ as well as the renamed functions mentioned above.
37
+ - In usages of the component `ErrorBoundary`, rename the property
38
+ `sendUsageData` to `sendTelemetryEvent`.
39
+
10
40
  ## 150.0.0 - 2024-01-18
11
41
 
12
42
  ### Removed
@@ -216,7 +246,7 @@ every new version is a new major version.
216
246
 
217
247
  ### Fixed
218
248
 
219
- - Telemtry: Metadata was not removed on request, when being in the main
249
+ - Telemetry: Metadata was not removed on request, when being in the main
220
250
  process. This is not critical because this code isn't yet executed in real
221
251
  life.
222
252
 
@@ -11,8 +11,8 @@ import path from 'path';
11
11
  import treeKill from 'tree-kill';
12
12
 
13
13
  import describeError from '../src/logging/describeError';
14
+ import telemetry from '../src/telemetry/telemetry';
14
15
  import { isDevelopment } from '../src/utils/environment';
15
- import usageData from '../src/utils/usageData';
16
16
  import { versionToInstall } from './moduleVersion';
17
17
  import { getNrfutilLogger } from './nrfutilLogger';
18
18
  import {
@@ -313,7 +313,7 @@ export class NrfutilSandbox {
313
313
  }
314
314
 
315
315
  error.message = error.message.replaceAll('Error: ', '');
316
- usageData.sendErrorReport(
316
+ telemetry.sendErrorReport(
317
317
  `${
318
318
  pid && this.logLevel === 'trace' ? `[PID:${pid}] ` : ''
319
319
  }${describeError(error)}`
@@ -332,7 +332,7 @@ export class NrfutilSandbox {
332
332
  ) =>
333
333
  new Promise<void>((resolve, reject) => {
334
334
  let aborting = false;
335
- usageData.sendUsageData(`running nrfutil ${this.module}`, {
335
+ telemetry.sendEvent(`running nrfutil ${this.module}`, {
336
336
  args,
337
337
  exec: command,
338
338
  });
@@ -494,7 +494,7 @@ export class NrfutilSandbox {
494
494
  }
495
495
 
496
496
  error.message = error.message.replaceAll('Error: ', '');
497
- usageData.sendErrorReport(
497
+ telemetry.sendErrorReport(
498
498
  `${
499
499
  pid && this.logLevel === 'trace' ? `[PID:${pid}] ` : ''
500
500
  }${describeError(error)}`
@@ -513,7 +513,7 @@ export class NrfutilSandbox {
513
513
  ) =>
514
514
  new Promise<void>((resolve, reject) => {
515
515
  let aborting = false;
516
- usageData.sendUsageData(`running nrfutil ${this.module}`, {
516
+ telemetry.sendEvent(`running nrfutil ${this.module}`, {
517
517
  args,
518
518
  exec: command,
519
519
  });
@@ -611,7 +611,7 @@ export class NrfutilSandbox {
611
611
  closedHandlers.forEach(callback => callback());
612
612
  })
613
613
  .catch(error => {
614
- usageData.sendErrorReport(describeError(error));
614
+ telemetry.sendErrorReport(describeError(error));
615
615
  running = false;
616
616
  closedHandlers.forEach(callback => callback(error));
617
617
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nordicsemiconductor/pc-nrfconnect-shared",
3
- "version": "150.0.0",
3
+ "version": "152.0.0",
4
4
  "description": "Shared commodities for developing pc-nrfconnect-* packages",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,11 +25,11 @@
25
25
  "prepare": "ts-node scripts/installHusky.ts",
26
26
  "release-shared": "ts-node scripts/release-shared.ts",
27
27
  "prepare-shared-release": "ts-node scripts/prepare-shared-release.ts",
28
- "clean": "rimraf dist typings/generated dist scripts/nordic-publish.js",
28
+ "clean": "rimraf dist typings/generated scripts/nordic-publish.js",
29
29
  "postinstall": "ts-node scripts/postinstall.ts"
30
30
  },
31
31
  "dependencies": {
32
- "@electron/remote": "^2.0.4",
32
+ "@electron/remote": "^2.1.1",
33
33
  "@mdi/font": "7.2.96",
34
34
  "@mdi/js": "^7.2.96",
35
35
  "@mdi/react": "^1.6.1",
@@ -62,7 +62,7 @@
62
62
  "bootstrap": "4.6.2",
63
63
  "commander": "10.0.0",
64
64
  "date-fns": "2.29.3",
65
- "electron": "22.3.5",
65
+ "electron": "^28.1.4",
66
66
  "electron-store": "8.1.0",
67
67
  "esbuild": "0.19.2",
68
68
  "esbuild-sass-plugin": "2.13.0",
@@ -5,8 +5,9 @@
5
5
  */
6
6
 
7
7
  import React from 'react';
8
- import { screen } from '@testing-library/react';
8
+ import { act, screen } from '@testing-library/react';
9
9
 
10
+ import { OFFICIAL } from '../../ipc/sources';
10
11
  import packageJsonFromShared from '../../package.json';
11
12
  import render from '../../test/testrenderer';
12
13
  import App, { Pane } from './App';
@@ -26,6 +27,12 @@ jest.mock('../utils/packageJson', () => ({
26
27
  packageJson: () => packageJsonFromShared,
27
28
  }));
28
29
 
30
+ const appDetails = Promise.resolve({ source: OFFICIAL });
31
+ jest.mock('../utils/appDetails', () => ({
32
+ __esModule: true,
33
+ default: () => appDetails,
34
+ }));
35
+
29
36
  const renderApp = (panes: Pane[]) => {
30
37
  const dummyReducer = (s = null) => s;
31
38
  const dummyNode = <div />;
@@ -50,9 +57,11 @@ const anotherPane = {
50
57
  };
51
58
 
52
59
  describe('App', () => {
53
- it('automatically gets an About pane attached', () => {
60
+ it('automatically gets an About pane attached', async () => {
54
61
  renderApp([aPane, anotherPane]);
55
62
 
56
63
  expect(screen.getByText('About')).toBeInTheDocument();
64
+
65
+ await act(() => appDetails);
57
66
  });
58
67
  });
package/src/App/App.tsx CHANGED
@@ -29,12 +29,12 @@ import FlashMessages from '../FlashMessage/FlashMessage';
29
29
  import LogViewer from '../Log/LogViewer';
30
30
  import logger from '../logging';
31
31
  import NavBar from '../NavBar/NavBar';
32
+ import telemetry from '../telemetry/telemetry';
32
33
  import classNames from '../utils/classNames';
33
34
  import {
34
35
  getPersistedCurrentPane,
35
36
  getPersistedLogVisible,
36
37
  } from '../utils/persistentStore';
37
- import usageData from '../utils/usageData';
38
38
  import useHotKey from '../utils/useHotKey';
39
39
  import {
40
40
  currentPane as currentPaneSelector,
@@ -87,7 +87,7 @@ const ConnectedApp: FC<ConnectedAppProps> = ({
87
87
  if (!initApp.current) {
88
88
  logger.initialise();
89
89
  setNrfutilLogger(logger);
90
- usageData.setLogger(logger);
90
+ telemetry.setLogger(logger);
91
91
  initApp.current = true;
92
92
  }
93
93
 
@@ -110,7 +110,7 @@ const ConnectedApp: FC<ConnectedAppProps> = ({
110
110
  }, [allPanes]);
111
111
 
112
112
  useEffect(() => {
113
- usageData.sendPageView(paneName.current[currentPane]);
113
+ telemetry.sendPageView(paneName.current[currentPane]);
114
114
  }, [currentPane]);
115
115
 
116
116
  useEffect(() => {
@@ -7,6 +7,8 @@
7
7
  import React from 'react';
8
8
  import { act, fireEvent, screen, waitFor } from '@testing-library/react';
9
9
 
10
+ import { OFFICIAL } from '../../../ipc/sources';
11
+ import packageJsonFromShared from '../../../package.json';
10
12
  import render from '../../../test/testrenderer';
11
13
  import { addDevice, Device, removeDevice } from '../deviceSlice';
12
14
  import { jprogDeviceSetup } from '../jprogOperations';
@@ -14,6 +16,17 @@ import DeviceSelector from './DeviceSelector';
14
16
 
15
17
  jest.mock('../../../nrfutil/device/device');
16
18
 
19
+ jest.mock('../../utils/packageJson', () => ({
20
+ isLauncher: () => false,
21
+ packageJson: () => packageJsonFromShared,
22
+ }));
23
+
24
+ const appDetails = Promise.resolve({ source: OFFICIAL });
25
+ jest.mock('../../utils/appDetails', () => ({
26
+ __esModule: true,
27
+ default: () => appDetails,
28
+ }));
29
+
17
30
  const DEVICE_SERIAL_NUMBER = '000000001';
18
31
 
19
32
  const testDevice: Device = {
@@ -10,8 +10,8 @@ import { useDispatch, useSelector } from 'react-redux';
10
10
  import { NrfutilDeviceLib } from '../../../nrfutil/device';
11
11
  import { DeviceTraits } from '../../../nrfutil/device/common';
12
12
  import logger from '../../logging';
13
- import usageData from '../../utils/usageData';
14
- import { simplifyDeviceForLogging } from '../../utils/usageDataCommon';
13
+ import simplifyDevice from '../../telemetry/simplifyDevice';
14
+ import telemetry from '../../telemetry/telemetry';
15
15
  import useHotKey from '../../utils/useHotKey';
16
16
  import {
17
17
  clearWaitForDevice,
@@ -71,9 +71,9 @@ export default ({
71
71
  const doDeselectDevice = useCallback(
72
72
  (device?: Device) => {
73
73
  if (device) {
74
- usageData.sendUsageData(
74
+ telemetry.sendEvent(
75
75
  'device deselected ',
76
- simplifyDeviceForLogging(device)
76
+ simplifyDevice(device)
77
77
  );
78
78
  }
79
79
 
@@ -120,8 +120,8 @@ export default ({
120
120
  dispatch(setSelectedDeviceInfo(deviceInfo));
121
121
  onDeviceSelected(device, autoReselected);
122
122
 
123
- usageData.sendUsageData('device selected', {
124
- device: simplifyDeviceForLogging(device),
123
+ telemetry.sendEvent('device selected', {
124
+ device: simplifyDevice(device),
125
125
  deviceInfo,
126
126
  });
127
127
 
@@ -9,8 +9,8 @@ import { DeviceTraits, NrfutilDevice } from '../../nrfutil/device/common';
9
9
  import NrfutilDeviceLib from '../../nrfutil/device/device';
10
10
  import logger from '../logging';
11
11
  import type { AppThunk, RootState } from '../store';
12
- import usageData from '../utils/usageData';
13
- import { simplifyDeviceForLogging } from '../utils/usageDataCommon';
12
+ import simplifyDevice from '../telemetry/simplifyDevice';
13
+ import telemetry from '../telemetry/telemetry';
14
14
  import {
15
15
  clearWaitForDevice,
16
16
  clearWaitForDeviceTimeout,
@@ -180,9 +180,9 @@ export const startWatchingDevices =
180
180
 
181
181
  const action = async (device: Device) => {
182
182
  if (hasValidDeviceTraits(device.traits, deviceListing)) {
183
- usageData.sendUsageData(
183
+ telemetry.sendEvent(
184
184
  'device connected',
185
- simplifyDeviceForLogging(device)
185
+ simplifyDevice(device)
186
186
  );
187
187
  if (
188
188
  !getState().device.devices.find(
@@ -13,8 +13,8 @@ import { generateSystemReport } from '../utils/systemReport';
13
13
  import ErrorBoundary from './ErrorBoundary';
14
14
 
15
15
  jest.mock('../utils/systemReport');
16
- jest.mock('../utils/usageData', () => ({
17
- ...jest.requireActual('../utils/usageData'),
16
+ jest.mock('../telemetry/telemetry', () => ({
17
+ ...jest.requireActual('../telemetry/telemetry'),
18
18
  sendErrorReport: jest.fn(),
19
19
  isEnabled: () => true,
20
20
  }));
@@ -44,14 +44,14 @@ describe('ErrorBoundary', () => {
44
44
  });
45
45
 
46
46
  it('can take custom reporting functions', () => {
47
- const sendUsageData = jest.fn();
47
+ const sendTelemetryEvent = jest.fn();
48
48
 
49
49
  render(
50
- <ErrorBoundary sendUsageData={sendUsageData}>
50
+ <ErrorBoundary sendTelemetryEvent={sendTelemetryEvent}>
51
51
  <Child />
52
52
  </ErrorBoundary>
53
53
  );
54
- expect(sendUsageData).toHaveBeenCalled();
54
+ expect(sendTelemetryEvent).toHaveBeenCalled();
55
55
  });
56
56
 
57
57
  it('should render error boundary component when there is an error', () => {
@@ -12,21 +12,17 @@ import { Device } from '../Device/deviceSlice';
12
12
  import FactoryResetButton from '../FactoryReset/FactoryResetButton';
13
13
  import { CollapsibleGroup } from '../SidePanel/Group';
14
14
  import Spinner from '../Spinner/Spinner';
15
+ import telemetry from '../telemetry/telemetry';
15
16
  import { openUrl } from '../utils/open';
16
17
  import { packageJson } from '../utils/packageJson';
17
18
  import { getAppSpecificStore as store } from '../utils/persistentStore';
18
19
  import { generateSystemReport } from '../utils/systemReport';
19
- import usageData from '../utils/usageData';
20
20
  import bugIcon from './bug.svg';
21
21
 
22
22
  import './error-boundary.scss';
23
23
 
24
- const sendGAEvent = (error: string) => {
25
- if (!usageData.isEnabled()) {
26
- return;
27
- }
28
-
29
- usageData.sendErrorReport(error);
24
+ const sendErrorReport = (error: string) => {
25
+ telemetry.sendErrorReport(error);
30
26
  };
31
27
 
32
28
  interface Props {
@@ -36,7 +32,7 @@ interface Props {
36
32
  devices?: Device[];
37
33
  appName?: string;
38
34
  restoreDefaults?: () => void;
39
- sendUsageData?: (message: string) => void;
35
+ sendTelemetryEvent?: (message: string) => void;
40
36
  }
41
37
 
42
38
  const genericRestoreDefaults = () => {
@@ -65,11 +61,16 @@ class ErrorBoundary extends React.Component<
65
61
  }
66
62
 
67
63
  componentDidCatch(error: Error) {
68
- const { devices, selectedDevice, selectedSerialNumber, sendUsageData } =
69
- this.props;
70
- sendUsageData != null
71
- ? sendUsageData(error.message)
72
- : sendGAEvent(error.message);
64
+ const {
65
+ devices,
66
+ selectedDevice,
67
+ selectedSerialNumber,
68
+ sendTelemetryEvent,
69
+ } = this.props;
70
+
71
+ sendTelemetryEvent != null
72
+ ? sendTelemetryEvent(error.message)
73
+ : sendErrorReport(error.message);
73
74
 
74
75
  generateSystemReport(
75
76
  new Date().toISOString().replace(/:/g, '-'),
@@ -39,15 +39,15 @@ export type XTerminalShellParser = ReturnType<
39
39
 
40
40
  export const xTerminalShellParserWrapper = (terminal: Terminal) => ({
41
41
  getTerminalData: () => {
42
- let out = '';
42
+ let result = '';
43
43
  for (let i = 0; i <= terminal.buffer.active.cursorY; i += 1) {
44
44
  const line = terminal.buffer.active.getLine(i);
45
45
  if (typeof line !== 'undefined') {
46
- out += `\r\n${line.translateToString()}`;
46
+ result += `\r\n${line.translateToString()}`;
47
47
  }
48
48
  }
49
49
 
50
- return out.trim();
50
+ return result.trim();
51
51
  },
52
52
  clear: () => terminal.clear(),
53
53
  getLastLine: () => {
package/src/index.ts CHANGED
@@ -77,8 +77,8 @@ export {
77
77
  export { openUrl, openFile } from './utils/open';
78
78
  export { default as systemReport } from './utils/systemReport';
79
79
 
80
- export { default as usageData } from './utils/usageData';
81
- export { type Metadata as UsageDataMetadata } from './utils/usageDataCommon';
80
+ export { default as telemetry } from './telemetry/telemetry';
81
+ export { type default as TelemetryMetadata } from './telemetry/TelemetryMetadata';
82
82
  export { default as classNames } from './utils/classNames';
83
83
  export { truncateMiddle } from './utils/truncateMiddle';
84
84
 
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Copyright (c) 2023 Nordic Semiconductor ASA
3
+ *
4
+ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
+ */
6
+
7
+ export default interface TelemetryMetadata {
8
+ [key: string]: unknown;
9
+ }
@@ -0,0 +1,80 @@
1
+ /*
2
+ * Copyright (c) 2023 Nordic Semiconductor ASA
3
+ *
4
+ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
+ */
6
+
7
+ import si from 'systeminformation';
8
+ import { Logger } from 'winston';
9
+
10
+ import {
11
+ deleteHasUserAgreedToTelemetry,
12
+ getHasUserAgreedToTelemetry,
13
+ persistHasUserAgreedToTelemetry,
14
+ } from '../utils/persistentStore';
15
+ import TelemetryMetadata from './TelemetryMetadata';
16
+
17
+ type MaybePromise<T> = T | Promise<T>;
18
+
19
+ export default abstract class TelemetrySender {
20
+ readonly INSTRUMENTATION_KEY = '4b8b1a39-37c7-479e-a684-d4763c7c647c';
21
+
22
+ logger?: Logger;
23
+ setLogger = (logger: Logger) => {
24
+ this.logger = logger;
25
+ };
26
+
27
+ isTelemetryAllowedForCurrentApp = false;
28
+ allowTelemetryForCurrentApp = () => {
29
+ this.isTelemetryAllowedForCurrentApp = true;
30
+ };
31
+
32
+ getIsSendingTelemetry = () =>
33
+ this.isTelemetryAllowedForCurrentApp &&
34
+ getHasUserAgreedToTelemetry() === true;
35
+
36
+ async sendAgreementEvent() {
37
+ this.sendEvent('Telemetry Opt-In');
38
+
39
+ const { platform, arch } = await si.osInfo();
40
+ this.sendEvent('Report OS info', { platform, arch });
41
+ }
42
+
43
+ sendDisagreementEvent() {
44
+ this.sendMinimalEvent('Telemetry Opt-Out');
45
+ }
46
+
47
+ async setUsersAgreedToTelemetry(hasAgreed: boolean) {
48
+ persistHasUserAgreedToTelemetry(hasAgreed);
49
+
50
+ if (hasAgreed) {
51
+ await this.sendAgreementEvent();
52
+ } else {
53
+ this.sendDisagreementEvent();
54
+ }
55
+
56
+ this.logger?.debug(
57
+ `Telemetry has been ${hasAgreed ? 'enabled' : 'disabled'}`
58
+ );
59
+ }
60
+
61
+ setUsersWithdrewTelemetryAgreement() {
62
+ deleteHasUserAgreedToTelemetry();
63
+ this.sendMinimalEvent('Telemetry Opt-Reset');
64
+ this.logger?.debug('Telemetry has been reset');
65
+ }
66
+
67
+ sendMinimalEvent(action: string) {
68
+ this.sendEvent(action, { removeAllMetadata: true });
69
+ }
70
+
71
+ abstract sendEvent(
72
+ action: string,
73
+ metadata?: TelemetryMetadata
74
+ ): MaybePromise<void>;
75
+ abstract sendPageView(pageName: string): MaybePromise<void>;
76
+ abstract sendMetric(name: string, average: number): MaybePromise<void>;
77
+ abstract sendTrace(message: string): MaybePromise<void>;
78
+ abstract sendErrorReport(error: Error): MaybePromise<void>;
79
+ abstract flush(): MaybePromise<void>;
80
+ }
@@ -0,0 +1,80 @@
1
+ /*
2
+ * Copyright (c) 2023 Nordic Semiconductor ASA
3
+ *
4
+ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
+ */
6
+
7
+ import { TelemetryClient } from 'applicationinsights';
8
+
9
+ import { isDevelopment } from '../utils/environment';
10
+ import { packageJson } from '../utils/packageJson';
11
+ import TelemetryMetadata from './TelemetryMetadata';
12
+ import TelemetrySender from './TelemetrySender';
13
+
14
+ export default class TelemetrySenderInMain extends TelemetrySender {
15
+ client?: TelemetryClient;
16
+
17
+ initClient() {
18
+ const appPackageJson = packageJson();
19
+ const applicationName = appPackageJson.name;
20
+ const applicationVersion = appPackageJson.version;
21
+
22
+ this.client = new TelemetryClient(this.INSTRUMENTATION_KEY);
23
+ this.client.config.enableAutoCollectConsole = false;
24
+ this.client.config.enableAutoCollectDependencies = false;
25
+ this.client.config.enableAutoCollectExceptions = false;
26
+ this.client.config.enableAutoCollectIncomingRequestAzureFunctions =
27
+ false;
28
+ this.client.config.enableAutoCollectHeartbeat = false;
29
+ this.client.config.enableAutoCollectPerformance = false;
30
+ this.client.config.enableAutoCollectPreAggregatedMetrics = false;
31
+ this.client.config.enableAutoCollectRequests = false;
32
+ this.client.config.enableAutoDependencyCorrelation = false;
33
+
34
+ // Add app name and version to every event
35
+ this.client.addTelemetryProcessor(envelope => {
36
+ if (envelope.data.baseData?.properties.removeAllMetadata) {
37
+ envelope.tags = [];
38
+ envelope.data.baseData = {
39
+ name: envelope.data?.baseData.name,
40
+ };
41
+ } else {
42
+ envelope.tags['ai.cloud.roleInstance'] = undefined; // remove PC name
43
+ if (envelope.data.baseData) {
44
+ envelope.data.baseData.properties = {
45
+ applicationName,
46
+ applicationVersion,
47
+ isDevelopment,
48
+ ...envelope.data.baseData.properties,
49
+ };
50
+ }
51
+ }
52
+
53
+ return true;
54
+ });
55
+
56
+ return this.client;
57
+ }
58
+
59
+ getClient = () => this.client ?? this.initClient();
60
+
61
+ sendEvent = (action: string, metadata?: TelemetryMetadata) => {
62
+ this.getClient().trackEvent({ name: action, properties: metadata });
63
+ this.logger?.debug(`Sending event ${JSON.stringify(action)}`);
64
+ };
65
+
66
+ sendPageView = (pageName: string) =>
67
+ this.getClient().trackPageView({ name: pageName });
68
+
69
+ sendMetric = (name: string, value: number) =>
70
+ this.getClient().trackMetric({ name, value });
71
+
72
+ sendTrace = (message: string) => this.getClient().trackTrace({ message });
73
+
74
+ sendErrorReport = (error: Error) => {
75
+ this.getClient().trackException({ exception: error });
76
+ this.logger?.error(error);
77
+ };
78
+
79
+ flush = () => this.getClient().flush();
80
+ }
@@ -0,0 +1,86 @@
1
+ /*
2
+ * Copyright (c) 2023 Nordic Semiconductor ASA
3
+ *
4
+ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
+ */
6
+
7
+ import { ApplicationInsights } from '@microsoft/applicationinsights-web';
8
+
9
+ import appDetails from '../utils/appDetails';
10
+ import { isDevelopment } from '../utils/environment';
11
+ import { isLauncher, packageJson } from '../utils/packageJson';
12
+ import { getTelemetryClientId } from '../utils/persistentStore';
13
+ import TelemetryMetadata from './TelemetryMetadata';
14
+ import TelemetrySender from './TelemetrySender';
15
+
16
+ export default class TelemetrySenderInRenderer extends TelemetrySender {
17
+ client?: ApplicationInsights;
18
+
19
+ async initClient() {
20
+ const source = isLauncher() ? undefined : (await appDetails()).source;
21
+
22
+ const applicationName = packageJson().name;
23
+ const applicationVersion = packageJson().version;
24
+
25
+ const accountId = getTelemetryClientId();
26
+
27
+ this.client = new ApplicationInsights({
28
+ config: {
29
+ instrumentationKey: this.INSTRUMENTATION_KEY,
30
+ accountId, // to hide with removeAllMetadata
31
+ isStorageUseDisabled: true, // fix issue with duplicate events being sent https://github.com/microsoft/ApplicationInsights-JS/issues/796
32
+ namePrefix: applicationName, // fix issue with duplicate events being sent https://github.com/microsoft/ApplicationInsights-JS/issues/796
33
+ },
34
+ });
35
+
36
+ this.client.loadAppInsights();
37
+
38
+ // Add app name and version to every event
39
+ this.client.addTelemetryInitializer(envelope => {
40
+ if (envelope.data?.removeAllMetadata) {
41
+ envelope.data = {};
42
+ envelope.baseData = { name: envelope.baseData?.name };
43
+ envelope.ext = {};
44
+ envelope.tags = [];
45
+ } else {
46
+ const trace = {
47
+ ...(envelope.ext?.trace ?? {}),
48
+ name: applicationName,
49
+ };
50
+ envelope.ext = { ...envelope.ext, trace };
51
+ envelope.data = {
52
+ ...envelope.data,
53
+ applicationName,
54
+ applicationVersion,
55
+ isDevelopment,
56
+ source,
57
+ };
58
+ }
59
+ });
60
+
61
+ return this.client;
62
+ }
63
+
64
+ getClient = () => this.client ?? this.initClient();
65
+
66
+ sendEvent = async (action: string, metadata?: TelemetryMetadata) => {
67
+ (await this.getClient()).trackEvent({ name: action }, metadata);
68
+ this.logger?.debug(`Sending event ${JSON.stringify(action)}`);
69
+ };
70
+
71
+ sendPageView = async (pageName: string) =>
72
+ (await this.getClient()).trackPageView({ name: pageName });
73
+
74
+ sendMetric = async (name: string, average: number) =>
75
+ (await this.getClient()).trackMetric({ name, average });
76
+
77
+ sendTrace = async (message: string) =>
78
+ (await this.getClient()).trackTrace({ message });
79
+
80
+ sendErrorReport = async (error: Error) => {
81
+ (await this.getClient()).trackException({ exception: error });
82
+ this.logger?.error(error);
83
+ };
84
+
85
+ flush = async () => (await this.getClient()).flush();
86
+ }