From 889c9dbbd4909eda23de838890b010c1455ebf77 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Wed, 26 Oct 2016 20:41:30 -0700 Subject: [PATCH] Reworked analytics --- app/backend/analytics.js | 6 +- app/backend/constants.js | 2 + app/backend/dns.js | 5 +- app/backend/ganalytics.js | 55 +++++++++++++++++++ app/localstorage.js | 10 ++++ app/main.html | 7 ++- app/main.js | 12 ++-- app/package.json | 2 + app/ui/components/RequestPane.js | 21 +++---- app/ui/components/ResponsePane.js | 17 +++--- app/ui/components/base/Editor.js | 2 - .../dropdowns/ContentTypeDropdown.js | 3 - .../dropdowns/PreviewModeDropdown.js | 3 - .../components/modals/RequestSwitcherModal.js | 4 +- app/ui/containers/App.js | 6 +- app/ui/index.js | 4 +- app/ui/redux/modules/global.js | 14 ++--- app/ui/redux/modules/requests.js | 6 +- package.json | 2 + scripts/sentry-release.sh | 8 +-- 20 files changed, 123 insertions(+), 66 deletions(-) create mode 100644 app/backend/ganalytics.js diff --git a/app/backend/analytics.js b/app/backend/analytics.js index 6764bcf81..c81c24927 100644 --- a/app/backend/analytics.js +++ b/app/backend/analytics.js @@ -6,7 +6,7 @@ import {SEGMENT_WRITE_KEY} from './constants'; let analytics = null; let userId = null; -export async function initAnalytics () { +export async function initLegacyAnalytics () { analytics = new Analytics(SEGMENT_WRITE_KEY); if (!userId) { @@ -14,7 +14,7 @@ export async function initAnalytics () { userId = stats._id; // Recurse now that we have a userId - return await initAnalytics(); + return await initLegacyAnalytics(); } analytics.identify({ @@ -31,7 +31,7 @@ export async function initAnalytics () { console.log(`-- Analytics Initialized for ${userId} --`); } -export function trackEvent (event, properties = {}) { +export function trackLegacyEvent (event, properties = {}) { // Don't track events if we haven't set them up yet if (analytics) { // Add base properties diff --git a/app/backend/constants.js b/app/backend/constants.js index a31453c1d..28d000876 100644 --- a/app/backend/constants.js +++ b/app/backend/constants.js @@ -6,6 +6,8 @@ export const LOCALSTORAGE_KEY = 'insomnia.state'; export const DB_PERSIST_INTERVAL = 1000 * 60 * 10; export const DEBOUNCE_MILLIS = 100; export const REQUEST_TIME_TO_SHOW_COUNTER = 1; // Seconds +export const GA_ID = 'UA-9837747-12'; +export const GA_HOST = 'desktop.insomnia.rest'; export const CHANGELOG_URL = 'https://changelog.insomnia.rest/changelog.json'; export const STATUS_CODE_PEBKAC = -333; export const LARGE_RESPONSE_MB = 10; diff --git a/app/backend/dns.js b/app/backend/dns.js index 487b0eeec..c2add0077 100644 --- a/app/backend/dns.js +++ b/app/backend/dns.js @@ -28,12 +28,11 @@ function lookup (url, forceIPv4) { } const v6 = results.find(r => r.family === 6); - const v4 = results.find(r => r.family === 4); - if (v6) { resolve(v6.address); } else { - resolve(v4.address); + // If no v6, return the first result + resolve(results[0].address); } }); }) diff --git a/app/backend/ganalytics.js b/app/backend/ganalytics.js new file mode 100644 index 000000000..00a01d452 --- /dev/null +++ b/app/backend/ganalytics.js @@ -0,0 +1,55 @@ +import * as constants from './constants'; +import {isDevelopment} from './appInfo'; + +let _ga = null; +let _sessionId = null; + +export function initAnalytics () { + if (isDevelopment()) { + console.log('-- Not initializing analytics for dev --'); + return; + } + + if (!_sessionId) { + _ga = _initGA(); + } + + if (!localStorage['gaClientId']) { + localStorage.setItem('gaClientId', require('node-uuid').v4()); + } + + const _sessionId = window.localStorage['gaClientId']; + + _ga('create', constants.GA_ID, {'storage': 'none', 'clientId': _sessionId}); + + // Disable URL protocol check + _ga('set', 'checkProtocolTask', () => null); + + // Set a fake location + _ga('set', 'location', `https://${constants.GA_HOST}/`); + + // Track the initial page view + _ga('send', 'pageview'); + + console.log(`-- Analytics Initialized for ${_sessionId} --`); +} + +export function trackEvent (category, action, label) { + _ga && _ga('send', 'event', category, action, label); +} + +function _initGA () { + (function (i, s, o, g, r, a, m) { + i['GoogleAnalyticsObject'] = r; + i[r] = i[r] || function () { + (i[r].q = i[r].q || []).push(arguments) + }, i[r].l = 1 * new Date(); + a = s.createElement(o), + m = s.getElementsByTagName(o)[0]; + a.async = 1; + a.src = g; + m.parentNode.insertBefore(a, m) + })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); + + return window.ga; +} diff --git a/app/localstorage.js b/app/localstorage.js index 451d65a69..af2fb8edf 100644 --- a/app/localstorage.js +++ b/app/localstorage.js @@ -1,10 +1,20 @@ const fs = require('fs'); const path = require('path'); +const mkdirp = require('mkdirp'); class LocalStorage { constructor (path) { this._path = path; this._timeouts = {}; + + // No need to wait for this. + mkdirp(path, err => { + if (err) { + console.warn('[localStorage] Failed to create directory', path, err); + } else { + console.log('[localStorage] initialized'); + } + }); } /** diff --git a/app/main.html b/app/main.html index c55750165..c32a7511f 100644 --- a/app/main.html +++ b/app/main.html @@ -3,6 +3,8 @@ Insomnia +
@@ -37,9 +39,8 @@ // Sentry if (process.env.INSOMNIA_ENV !== 'development') { - Raven.config('https://fb3242f902b54cdd934b8ffa204426c0:23430fbe203a4189a68efb63c38fc50b@app.getsentry.com/88289', { - allowSecretKey: true, - logger: 'renderer', + Raven.config('https://786e43ae199c4757a9ea4a48a9abd17d@sentry.io/109702', { + logger: 'electron.renderer', environment: process.env.INSOMNIA_ENV || 'production', level: 'warning', release: require('./package.json').version, diff --git a/app/main.js b/app/main.js index 67cb61eff..bb158efaa 100644 --- a/app/main.js +++ b/app/main.js @@ -12,11 +12,10 @@ const IS_LINUX = !IS_MAC && !IS_WINDOWS; var raven = require('raven'); var ravenClient = new raven.Client( - 'https://fb3242f902b54cdd934b8ffa204426c0:23430fbe203a' + - '4189a68efb63c38fc50b@app.getsentry.com/88289', { + 'https://786e43ae199c4757a9ea4a48a9abd17d@sentry.io/109702', { environment: process.env.INSOMNIA_ENV || 'production', release: require('./package.json').version, - logger: 'main' + logger: 'electron.main' }); if (!IS_DEV) { @@ -66,13 +65,10 @@ process.on('uncaughtException', e => { }); autoUpdater.on('error', e => { - // Failed to launch auto updater if (IS_DEV) { console.error(e); - } else if (e.toString().indexOf('')) { - ravenClient.captureError(e, { - level: 'warning' - }); + } else { + // Don't report autoUpdater error. They are way too noisy } }); diff --git a/app/package.json b/app/package.json index 4265e5db5..403640005 100644 --- a/app/package.json +++ b/app/package.json @@ -11,11 +11,13 @@ "dependencies": { "analytics-node": "^2.1.0", "electron-context-menu": "^0.4.0", + "electron-cookies": "^1.1.0", "electron-squirrel-startup": "^1.0.0", "hkdf": "0.0.2", "httpsnippet": "git@github.com:getinsomnia/httpsnippet.git#a3a2c0a0167fa844bf92df52a1442fa1d68a9053", "json-lint": "^0.1.0", "jsonpath-plus": "^0.15.0", + "mkdirp": "^0.5.1", "nedb": "^1.8.0", "node-forge": "^0.6.43", "node-uuid": "^1.4.7", diff --git a/app/ui/components/RequestPane.js b/app/ui/components/RequestPane.js index c4c2f24ab..8d1225120 100644 --- a/app/ui/components/RequestPane.js +++ b/app/ui/components/RequestPane.js @@ -1,6 +1,5 @@ import React, {Component, PropTypes} from 'react'; import {Tab, Tabs, TabList, TabPanel} from 'react-tabs'; - import KeyValueEditor from './base/KeyValueEditor'; import RequestHeadersEditor from './editors/RequestHeadersEditor'; import ContentTypeDropdown from './dropdowns/ContentTypeDropdown'; @@ -9,11 +8,11 @@ import BodyEditor from './editors/BodyEditor'; import AuthEditor from './editors/AuthEditor'; import {UrlBar} from './UrlBar.elm'; import ElmComponent from './ElmComponent'; - -import {getContentTypeName} from '../../backend/contentTypes'; -import {getContentTypeFromHeaders} from '../../backend/contentTypes'; +import { + getContentTypeName, + getContentTypeFromHeaders +} from '../../backend/contentTypes'; import {MOD_SYM} from '../../backend/constants'; -import {trackEvent} from '../../backend/analytics'; import {debounce} from '../lib/debounce'; class RequestPane extends Component { @@ -104,31 +103,27 @@ class RequestPane extends Component { - - - - - - diff --git a/app/ui/components/dropdowns/PreviewModeDropdown.js b/app/ui/components/dropdowns/PreviewModeDropdown.js index 7aecf9748..d6b1ab7ef 100644 --- a/app/ui/components/dropdowns/PreviewModeDropdown.js +++ b/app/ui/components/dropdowns/PreviewModeDropdown.js @@ -1,8 +1,6 @@ import React, {PropTypes} from 'react'; - import Dropdown from '../base/Dropdown'; import {PREVIEW_MODES, getPreviewModeName} from '../../../backend/previewModes'; -import {trackEvent} from '../../../backend/analytics'; const PreviewModeDropdown = ({updatePreviewMode}) => ( @@ -13,7 +11,6 @@ const PreviewModeDropdown = ({updatePreviewMode}) => ( {PREVIEW_MODES.map(previewMode => (
  • diff --git a/app/ui/components/modals/RequestSwitcherModal.js b/app/ui/components/modals/RequestSwitcherModal.js index 7220c06e9..a7ab35968 100644 --- a/app/ui/components/modals/RequestSwitcherModal.js +++ b/app/ui/components/modals/RequestSwitcherModal.js @@ -6,7 +6,7 @@ import ModalHeader from '../base/ModalHeader'; import ModalBody from '../base/ModalBody'; import MethodTag from '../tags/MethodTag'; import * as db from '../../../backend/database'; -import {trackEvent} from '../../../backend/analytics'; +import {trackLegacyEvent} from '../../../backend/analytics'; class RequestSwitcherModal extends Component { @@ -165,7 +165,7 @@ class RequestSwitcherModal extends Component { } show () { - trackEvent('Show Quick Switcher'); + trackEvent('Modal', 'Show', 'Quick Switcher'); this.modal.show(); this._handleChange(''); } diff --git a/app/ui/containers/App.js b/app/ui/containers/App.js index c0bc3fbd8..0278eadd1 100644 --- a/app/ui/containers/App.js +++ b/app/ui/containers/App.js @@ -36,7 +36,7 @@ import * as GlobalActions from '../redux/modules/global'; import * as RequestActions from '../redux/modules/requests'; import * as db from '../../backend/database'; import {importCurl} from '../../backend/export/curl'; -import {trackEvent} from '../../backend/analytics'; +import {trackLegacyEvent} from '../../backend/analytics'; import {getAppVersion} from '../../backend/appInfo'; import {getModal} from '../components/modals/index'; @@ -420,14 +420,14 @@ class App extends Component { }); // Do The Analytics - trackEvent('App Launched'); + trackLegacyEvent('App Launched'); // Update Stats Object const {lastVersion, launches} = await db.stats.get(); const firstLaunch = !lastVersion; if (firstLaunch) { // TODO: Show a welcome message - trackEvent('First Launch'); + trackLegacyEvent('First Launch'); } else if (lastVersion !== getAppVersion()) { getModal(ChangelogModal).show(); } diff --git a/app/ui/index.js b/app/ui/index.js index e9847a24c..f65a69735 100644 --- a/app/ui/index.js +++ b/app/ui/index.js @@ -15,7 +15,8 @@ import {initStore} from './redux/initstore'; import {initDB} from '../backend/database'; import {initSync} from '../backend/sync'; import {getAppVersion} from '../backend/appInfo'; -import {initAnalytics} from '../backend/analytics'; +import {initLegacyAnalytics} from '../backend/analytics'; +import {initAnalytics} from '../backend/ganalytics'; // Don't inject component styles (use our own) Tabs.setUseDefaultStyles(false); @@ -29,6 +30,7 @@ console.log(`-- Loading App v${getAppVersion()} --`); await initSync(); await initStore(store.dispatch); await initAnalytics(); + await initLegacyAnalytics(); console.log('-- Rendering App --'); render( , diff --git a/app/ui/redux/modules/global.js b/app/ui/redux/modules/global.js index b3c8189ee..8fb82d7fc 100644 --- a/app/ui/redux/modules/global.js +++ b/app/ui/redux/modules/global.js @@ -3,7 +3,7 @@ import {combineReducers} from 'redux'; import fs from 'fs'; import {importJSON, exportJSON} from '../../../backend/export/database'; -import {trackEvent} from '../../../backend/analytics'; +import {trackEvent} from '../../../backend/ganalytics'; const LOAD_START = 'global/load-start'; const LOAD_STOP = 'global/load-stop'; @@ -123,7 +123,7 @@ export function importFile (workspace) { if (!paths) { // It was cancelled, so let's bail out dispatch(loadStop()); - trackEvent('Import Cancel'); + trackEvent('import', 'Cancel'); return; } @@ -133,13 +133,13 @@ export function importFile (workspace) { dispatch(loadStop()); if (err) { - trackEvent('Import Fail'); + trackEvent('Import', 'Failure'); console.warn('Import Failed', err); return; } importJSON(workspace, data); - trackEvent('Import'); + trackEvent('Import', 'Success'); }); }) }); @@ -200,7 +200,7 @@ export function exportFile (parentDoc = null) { electron.remote.dialog.showSaveDialog(options, filename => { if (!filename) { - trackEvent('Export Cancel'); + trackEvent('Export', 'Cancel'); // It was cancelled, so let's bail out dispatch(loadStop()); return; @@ -209,10 +209,10 @@ export function exportFile (parentDoc = null) { fs.writeFile(filename, json, {}, err => { if (err) { console.warn('Export failed', err); - trackEvent('Export Fail'); + trackEvent('Export', 'Failure'); return; } - trackEvent('Export'); + trackEvent('Export', 'Success'); dispatch(loadStop()); }); }); diff --git a/app/ui/redux/modules/requests.js b/app/ui/redux/modules/requests.js index 9ba19e2d4..fec237a72 100644 --- a/app/ui/redux/modules/requests.js +++ b/app/ui/redux/modules/requests.js @@ -1,7 +1,8 @@ import {combineReducers} from 'redux'; -import {trackEvent} from '../../../backend/analytics'; +import {trackLegacyEvent} from '../../../backend/analytics'; import * as network from '../../../backend/network'; +import {trackEvent} from '../../../backend/ganalytics'; export const REQUEST_SEND_START = 'requests/start'; export const REQUEST_SEND_STOP = 'requests/stop'; @@ -44,7 +45,8 @@ export function send(request) { return async function (dispatch) { dispatch({type: REQUEST_SEND_START, requestId: request._id}); - trackEvent('Request Send'); + trackEvent('Request', 'Send'); + trackLegacyEvent('Request Send'); try { await network.send(request._id); diff --git a/package.json b/package.json index f00150a8c..928f1b95a 100644 --- a/package.json +++ b/package.json @@ -61,11 +61,13 @@ "classnames": "^2.2.3", "codemirror": "^5.18.2", "electron-context-menu": "^0.4.0", + "electron-cookies": "^1.1.0", "electron-squirrel-startup": "^1.0.0", "hkdf": "0.0.2", "httpsnippet": "git@github.com:getinsomnia/httpsnippet.git#a3a2c0a0167fa844bf92df52a1442fa1d68a9053", "json-lint": "^0.1.0", "jsonpath-plus": "^0.15.0", + "mkdirp": "^0.5.1", "mousetrap": "^1.6.0", "nedb": "^1.8.0", "node-forge": "^0.6.43", diff --git a/scripts/sentry-release.sh b/scripts/sentry-release.sh index ffed26227..ca296112f 100755 --- a/scripts/sentry-release.sh +++ b/scripts/sentry-release.sh @@ -10,7 +10,7 @@ echo "-- Creating Release $APP_VERSION --" # Create a new release # # ~~~~~~~~~~~~~~~~~~~~ # -curl https://app.getsentry.com/api/0/projects/schierco/insomnia-electron/releases/ \ +curl https://app.getsentry.com/api/0/projects/schierco/insomnia-app/releases/ \ -X POST \ -u "$SENTRY_TOKEN:" \ -H 'Content-Type: application/json' \ @@ -23,20 +23,20 @@ echo "-- Uploading Source Maps for $APP_VERSION --" # Upload files for the given release # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -curl https://app.getsentry.com/api/0/projects/schierco/insomnia-electron/releases/${APP_VERSION}/files/ \ +curl https://app.getsentry.com/api/0/projects/schierco/insomnia-app/releases/${APP_VERSION}/files/ \ -X POST \ -u "$SENTRY_TOKEN:" \ -F file=@./build/bundle.js \ -F name="bundle.js" -curl https://app.getsentry.com/api/0/projects/schierco/insomnia-electron/releases/${APP_VERSION}/files/ \ +curl https://app.getsentry.com/api/0/projects/schierco/insomnia-app/releases/${APP_VERSION}/files/ \ -X POST \ -u "$SENTRY_TOKEN:" \ -F file=@./build/bundle.js \ -F name="bundle.js" # Upload a file for the given release -curl https://app.getsentry.com/api/0/projects/schierco/insomnia-electron/releases/${APP_VERSION}/files/ \ +curl https://app.getsentry.com/api/0/projects/schierco/insomnia-app/releases/${APP_VERSION}/files/ \ -X POST \ -u "$SENTRY_TOKEN:" \ -F file=@./build/bundle.js.map \