2017-11-18 22:47:54 +00:00
|
|
|
// @flow
|
|
|
|
import * as models from '../models/index';
|
|
|
|
import * as electron from 'electron';
|
|
|
|
import uuid from 'uuid';
|
|
|
|
import {GA_ID, GA_LOCATION, getAppPlatform, getAppVersion, isDevelopment} from './constants';
|
|
|
|
import {getAccountId} from '../sync/session';
|
|
|
|
import type {RequestParameter} from '../models/request';
|
|
|
|
import * as querystring from './querystring';
|
|
|
|
import {getScreenResolution, getUserLanguage, getViewportSize} from './misc';
|
|
|
|
|
|
|
|
const DIMENSION_PLATFORM = 1;
|
|
|
|
const DIMENSION_VERSION = 2;
|
|
|
|
|
|
|
|
const KEY_TRACKING_ID = 'tid';
|
|
|
|
const KEY_VERSION = 'v';
|
|
|
|
const KEY_CLIENT_ID = 'cid';
|
|
|
|
const KEY_USER_ID = 'uid';
|
|
|
|
const KEY_HIT_TYPE = 't';
|
|
|
|
const KEY_LOCATION = 'dl';
|
|
|
|
const KEY_TITLE = 'dt';
|
|
|
|
const KEY_NON_INTERACTION = 'ni';
|
|
|
|
const KEY_VIEWPORT_SIZE = 'vp';
|
|
|
|
const KEY_SCREEN_RESOLUTION = 'sr';
|
|
|
|
const KEY_USER_LANGUAGE = 'ul';
|
|
|
|
const KEY_DOCUMENT_ENCODING = 'de';
|
|
|
|
const KEY_EVENT_CATEGORY = 'ec';
|
|
|
|
const KEY_EVENT_ACTION = 'ea';
|
|
|
|
const KEY_EVENT_LABEL = 'el';
|
|
|
|
const KEY_EVENT_VALUE = 'ev';
|
|
|
|
|
|
|
|
const KEY_CUSTOM_DIMENSION_PREFIX = 'cd';
|
|
|
|
|
|
|
|
export function trackEvent (
|
|
|
|
category: string,
|
|
|
|
action: string,
|
|
|
|
label: ?string,
|
|
|
|
value: ?string
|
|
|
|
) {
|
|
|
|
process.nextTick(async () => {
|
|
|
|
await _trackEvent(true, category, action, label, value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function trackNonInteractiveEvent (
|
|
|
|
category: string,
|
|
|
|
action: string,
|
|
|
|
label: ?string,
|
|
|
|
value: ?string
|
|
|
|
) {
|
|
|
|
process.nextTick(async () => {
|
|
|
|
await _trackEvent(false, category, action, label, value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function trackPageView () {
|
|
|
|
await _trackPageView();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ~~~~~~~~~~~~~~~~~ //
|
|
|
|
// Private Functions //
|
|
|
|
// ~~~~~~~~~~~~~~~~~ //
|
|
|
|
|
|
|
|
// Exported for testing
|
|
|
|
export async function _trackEvent (
|
|
|
|
interactive: boolean,
|
|
|
|
category: string,
|
|
|
|
action: string,
|
|
|
|
label: ?string,
|
|
|
|
value: ?string
|
|
|
|
) {
|
|
|
|
const prefix = interactive ? '[ga] Event' : '[ga] Non-interactive';
|
2017-11-22 00:07:28 +00:00
|
|
|
console.log(prefix, [category, action, label, value].filter(Boolean).join(', '));
|
2017-11-18 22:47:54 +00:00
|
|
|
|
|
|
|
const params = [
|
|
|
|
{name: KEY_HIT_TYPE, value: 'event'},
|
|
|
|
{name: KEY_EVENT_CATEGORY, value: category},
|
|
|
|
{name: KEY_EVENT_ACTION, value: action}
|
|
|
|
];
|
|
|
|
|
|
|
|
!interactive && params.push({name: KEY_NON_INTERACTION, value: '1'});
|
|
|
|
label && params.push({name: KEY_EVENT_LABEL, value: label});
|
|
|
|
value && params.push({name: KEY_EVENT_VALUE, value: value});
|
|
|
|
|
|
|
|
await _sendToGoogle(params);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function _trackPageView () {
|
|
|
|
const params = [{name: KEY_HIT_TYPE, value: 'pageview'}];
|
2017-11-22 00:07:28 +00:00
|
|
|
console.log('[ga] Page', GA_LOCATION);
|
2017-11-18 22:47:54 +00:00
|
|
|
await _sendToGoogle(params);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function _getDefaultParams (): Promise<Array<RequestParameter>> {
|
|
|
|
let settings = await models.settings.getOrCreate();
|
|
|
|
const accountId = getAccountId();
|
|
|
|
|
|
|
|
// Migrate old GA ID into settings model
|
|
|
|
let {deviceId} = settings;
|
|
|
|
if (!deviceId) {
|
2017-11-21 17:49:33 +00:00
|
|
|
const oldId = (window && window.localStorage['gaClientId']) || null;
|
2017-11-18 22:47:54 +00:00
|
|
|
deviceId = oldId || uuid.v4();
|
|
|
|
await models.settings.update(settings, {deviceId});
|
|
|
|
}
|
|
|
|
|
|
|
|
const params = [
|
|
|
|
{name: KEY_VERSION, value: '1'},
|
|
|
|
{name: KEY_TRACKING_ID, value: GA_ID},
|
|
|
|
{name: KEY_CLIENT_ID, value: deviceId},
|
|
|
|
{name: KEY_LOCATION, value: GA_LOCATION},
|
|
|
|
{name: KEY_SCREEN_RESOLUTION, value: getScreenResolution()},
|
|
|
|
{name: KEY_USER_LANGUAGE, value: getUserLanguage()},
|
|
|
|
{name: KEY_TITLE, value: `Insomnia ${getAppVersion()}`},
|
|
|
|
{name: KEY_CUSTOM_DIMENSION_PREFIX + DIMENSION_PLATFORM, value: getAppPlatform()},
|
|
|
|
{name: KEY_CUSTOM_DIMENSION_PREFIX + DIMENSION_VERSION, value: getAppVersion()}
|
|
|
|
];
|
|
|
|
|
|
|
|
const viewport = getViewportSize();
|
|
|
|
viewport && params.push({name: KEY_VIEWPORT_SIZE, value: viewport});
|
|
|
|
|
|
|
|
accountId && params.push({name: KEY_USER_ID, value: accountId});
|
|
|
|
global.document && params.push({
|
|
|
|
name: KEY_DOCUMENT_ENCODING,
|
|
|
|
value: global.document.inputEncoding
|
|
|
|
});
|
|
|
|
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function _sendToGoogle (params: Array<RequestParameter>) {
|
|
|
|
let settings = await models.settings.getOrCreate();
|
|
|
|
if (settings.disableAnalyticsTracking) {
|
2017-11-22 00:07:28 +00:00
|
|
|
console.log('[ga] Google analytics tracking disabled. Not sending');
|
2017-11-18 22:47:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const baseParams = await _getDefaultParams();
|
|
|
|
const allParams = [...baseParams, ...params];
|
|
|
|
const qs = querystring.buildFromParams(allParams);
|
|
|
|
const baseUrl = isDevelopment()
|
|
|
|
? 'https://www.google-analytics.com/debug/collect'
|
|
|
|
: 'https://www.google-analytics.com/collect';
|
|
|
|
const url = querystring.joinUrl(baseUrl, qs);
|
|
|
|
|
|
|
|
const net = (electron.remote || electron).net;
|
|
|
|
const request = net.request(url);
|
|
|
|
|
|
|
|
request.once('error', err => {
|
|
|
|
console.warn('[ga] Network error', err);
|
|
|
|
});
|
|
|
|
|
|
|
|
request.once('response', response => {
|
|
|
|
const {statusCode} = response;
|
|
|
|
if (statusCode < 200 && statusCode >= 300) {
|
|
|
|
console.warn('[ga] Bad status code ' + statusCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
const chunks = [];
|
2017-11-20 16:07:36 +00:00
|
|
|
const [contentType] = response.headers['content-type'] || [];
|
|
|
|
|
|
|
|
if (contentType !== 'application/json') {
|
|
|
|
// Production GA API returns a Gif to use for tracking
|
|
|
|
return;
|
|
|
|
}
|
2017-11-18 22:47:54 +00:00
|
|
|
|
|
|
|
response.on('end', () => {
|
2017-11-20 16:07:36 +00:00
|
|
|
const jsonStr = Buffer.concat(chunks).toString('utf8');
|
2017-11-18 22:47:54 +00:00
|
|
|
try {
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
const {hitParsingResult} = data;
|
|
|
|
if (hitParsingResult.valid) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const result of hitParsingResult || []) {
|
|
|
|
for (const msg of result.parserMessage || []) {
|
|
|
|
console.warn(`[ga] Error ${msg.description}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
console.warn('[ga] Failed to parse response', err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
response.on('data', chunk => {
|
|
|
|
chunks.push(chunk);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
request.end();
|
|
|
|
}
|