From 4b883f68ec4a832ff1eb22fb4a531a8c3999acac Mon Sep 17 00:00:00 2001 From: Aleck Landgraf Date: Wed, 5 Jun 2019 12:07:52 -0700 Subject: [PATCH] update(Metrics): Set Google Analytics userId if able. (#75) * update(Metrics): set Google Analytics user ID if able trust the function [Metrics] add google analytics types, update tests [Metrics] rename to bootstrapGoogleAnalyticsUser fix package-lock clean up a couple things * empty commit to bypass danger * fix test name * update types and tests * fix typing of jest.spy * downgrade npm version to remove option=true --- .eslintrc.js | 1 + package-lock.json | 6 ++++ package.json | 1 + packages/metrics/README.md | 3 ++ packages/metrics/src/index.ts | 8 +++++ .../metrics/src/utils/hasGoogleAnalytics.ts | 3 ++ packages/metrics/test/index.test.ts | 31 +++++++++++++++++++ types/global.d.ts | 1 + 8 files changed, 54 insertions(+) create mode 100644 packages/metrics/src/utils/hasGoogleAnalytics.ts diff --git a/.eslintrc.js b/.eslintrc.js index 7b0f470ee..9dceffee0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,6 +11,7 @@ module.exports = { __DEV__: true, jsdom: true, newrelic: true, + ga: true, }, env: { diff --git a/package-lock.json b/package-lock.json index ba85d047d..d40d63d89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3351,6 +3351,12 @@ "resolved": "https://registry.npmjs.org/@types/extend/-/extend-3.0.1.tgz", "integrity": "sha512-R1g/VyKFFI2HLC1QGAeTtCBWCo6n75l41OnsVYNbmKG+kempOESaodf6BeJyUM3Q0rKa/NQcTHbB2+66lNnxLw==" }, + "@types/google.analytics": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/google.analytics/-/google.analytics-0.0.39.tgz", + "integrity": "sha512-AwVtVYACQg26MJz+752H6uskn53BR7eilCOHksUGkzobZNKc7O3RFTJrbD3yKAluXy6favVdynnr9btijjcakQ==", + "dev": true + }, "@types/graphql": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.2.0.tgz", diff --git a/package.json b/package.json index fa6796c47..8b21251c5 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.3.3", "@types/enzyme": "^3.9.1", + "@types/google.analytics": "0.0.39", "@types/jest": "^24.0.11", "@types/storybook__addon-a11y": "^5.0.0", "@types/storybook__addon-actions": "^3.4.2", diff --git a/packages/metrics/README.md b/packages/metrics/README.md index aa013edae..37337f63e 100644 --- a/packages/metrics/README.md +++ b/packages/metrics/README.md @@ -3,6 +3,9 @@ Provides integrated [NewRelic](https://newrelic.com/) and [Sentry](https://sentry.io/welcome/) insights and metrics logging. +This will also set the Google Analytics `userId` if `ga` is global and `userID` is passed to +`Metrics.initialize`. + ```bash static npm install @airbnb/lunar-metrics --save ``` diff --git a/packages/metrics/src/index.ts b/packages/metrics/src/index.ts index 963681fab..497211487 100644 --- a/packages/metrics/src/index.ts +++ b/packages/metrics/src/index.ts @@ -1,5 +1,6 @@ import Raven from 'raven-js'; import hasNewRelic from './utils/hasNewRelic'; +import hasGoogleAnalytics from './utils/hasGoogleAnalytics'; export type IgnoreError = string | RegExp; @@ -28,6 +29,7 @@ class Metrics { this.bootstrapNewRelic(); this.bootstrapSentry(); + this.bootstrapGoogleAnalyticsUser(); } bootstrapNewRelic() { @@ -82,6 +84,12 @@ class Metrics { }) .install(); } + + bootstrapGoogleAnalyticsUser() { + if (hasGoogleAnalytics() && this.settings.userID) { + ga('set', 'userId', this.settings.userID); + } + } } export default new Metrics(); diff --git a/packages/metrics/src/utils/hasGoogleAnalytics.ts b/packages/metrics/src/utils/hasGoogleAnalytics.ts new file mode 100644 index 000000000..94ab8a5c7 --- /dev/null +++ b/packages/metrics/src/utils/hasGoogleAnalytics.ts @@ -0,0 +1,3 @@ +export default function hasGoogleAnalytics() { + return !!ga && typeof ga === 'function'; +} diff --git a/packages/metrics/test/index.test.ts b/packages/metrics/test/index.test.ts index c7520347e..b849b9be9 100644 --- a/packages/metrics/test/index.test.ts +++ b/packages/metrics/test/index.test.ts @@ -31,6 +31,8 @@ describe('Metrics', () => { global.newrelic.setCustomAttribute = jest.fn(); global.newrelic.setErrorHandler = jest.fn(); + + global.ga = (jest.fn() as unknown) as UniversalAnalytics.ga; }); afterEach(() => { @@ -56,14 +58,17 @@ describe('Metrics', () => { it('calls boostrap functions', () => { const nrBoot = jest.spyOn(Metrics, 'bootstrapNewRelic'); const sentryBoot = jest.spyOn(Metrics, 'bootstrapSentry'); + const bootstrapGoogleAnalyticsUser = jest.spyOn(Metrics, 'bootstrapGoogleAnalyticsUser'); Metrics.initialize(); expect(nrBoot).toHaveBeenCalled(); expect(sentryBoot).toHaveBeenCalled(); + expect(bootstrapGoogleAnalyticsUser).toHaveBeenCalled(); nrBoot.mockRestore(); sentryBoot.mockRestore(); + bootstrapGoogleAnalyticsUser.mockRestore(); }); }); @@ -152,4 +157,30 @@ describe('Metrics', () => { ); }); }); + + describe('bootstrapGoogleAnalyticsUser', () => { + it('sets the google analytics user if present', () => { + Metrics.settings.userID = 12355; + Metrics.bootstrapGoogleAnalyticsUser(); + + expect(global.ga).toBeCalledTimes(1); + expect(global.ga).toBeCalledWith('set', 'userId', 12355); + }); + + it('does not attempt to set the google analytics user if the user ID is not present', () => { + Metrics.settings.userID = null; + Metrics.bootstrapGoogleAnalyticsUser(); + + expect(global.ga).not.toHaveBeenCalled(); + }); + + it('does not attempt to set the google analytics user if ga is not present', () => { + Metrics.settings.userID = 123; + global.ga = undefined; + + expect(() => { + Metrics.bootstrapGoogleAnalyticsUser(); + }).not.toThrow(); + }); + }); }); diff --git a/types/global.d.ts b/types/global.d.ts index c106475ec..c4bfa0f5c 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -75,6 +75,7 @@ interface KeyboardEvent { declare namespace NodeJS { interface Global { __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any; + ga?: UniversalAnalytics.ga; location: Location; navigator: Navigator; newrelic: NewRelic.Browser;